refactor(preference): better readability

This commit is contained in:
grandwizard28 2025-06-04 00:59:02 +05:30
parent 6ed30318bd
commit a7d0ae21ac
No known key found for this signature in database
GPG Key ID: C7A26EDC5B7054B7
10 changed files with 487 additions and 540 deletions

View File

@ -11,6 +11,7 @@ import (
"github.com/SigNoz/signoz/pkg/modules/preference"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/preferencetypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/gorilla/mux"
)
@ -22,7 +23,7 @@ func NewHandler(module preference.Module) preference.Handler {
return &handler{module: module}
}
func (handler *handler) GetOrg(rw http.ResponseWriter, r *http.Request) {
func (handler *handler) ListByOrg(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
@ -32,65 +33,7 @@ func (handler *handler) GetOrg(rw http.ResponseWriter, r *http.Request) {
return
}
id, ok := mux.Vars(r)["preferenceId"]
if !ok {
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required"))
return
}
preference, err := handler.module.GetOrg(ctx, id, claims.OrgID)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, preference)
}
func (handler *handler) UpdateOrg(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
id, ok := mux.Vars(r)["preferenceId"]
if !ok {
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required"))
return
}
req := new(preferencetypes.UpdatablePreference)
err = json.NewDecoder(r.Body).Decode(req)
if err != nil {
render.Error(rw, err)
return
}
err = handler.module.UpdateOrg(ctx, id, req.PreferenceValue, claims.OrgID)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusNoContent, nil)
}
func (handler *handler) GetAllOrg(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
preferences, err := handler.module.GetAllOrg(ctx, claims.OrgID)
preferences, err := handler.module.ListByOrg(ctx, valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, err)
return
@ -99,7 +42,7 @@ func (handler *handler) GetAllOrg(rw http.ResponseWriter, r *http.Request) {
render.Success(rw, http.StatusOK, preferences)
}
func (handler *handler) GetUser(rw http.ResponseWriter, r *http.Request) {
func (handler *handler) GetByOrg(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
@ -109,13 +52,19 @@ func (handler *handler) GetUser(rw http.ResponseWriter, r *http.Request) {
return
}
id, ok := mux.Vars(r)["preferenceId"]
nameString, ok := mux.Vars(r)["preferenceId"]
if !ok {
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required"))
return
}
preference, err := handler.module.GetUser(ctx, id, claims.OrgID, claims.UserID)
name, err := preferencetypes.NewName(nameString)
if err != nil {
render.Error(rw, err)
return
}
preference, err := handler.module.GetByOrg(ctx, valuer.MustNewUUID(claims.OrgID), name)
if err != nil {
render.Error(rw, err)
return
@ -124,7 +73,7 @@ func (handler *handler) GetUser(rw http.ResponseWriter, r *http.Request) {
render.Success(rw, http.StatusOK, preference)
}
func (handler *handler) UpdateUser(rw http.ResponseWriter, r *http.Request) {
func (handler *handler) UpdateByOrg(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
@ -134,12 +83,18 @@ func (handler *handler) UpdateUser(rw http.ResponseWriter, r *http.Request) {
return
}
id, ok := mux.Vars(r)["preferenceId"]
nameString, ok := mux.Vars(r)["preferenceId"]
if !ok {
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required"))
return
}
name, err := preferencetypes.NewName(nameString)
if err != nil {
render.Error(rw, err)
return
}
req := new(preferencetypes.UpdatablePreference)
err = json.NewDecoder(r.Body).Decode(req)
if err != nil {
@ -147,7 +102,7 @@ func (handler *handler) UpdateUser(rw http.ResponseWriter, r *http.Request) {
return
}
err = handler.module.UpdateUser(ctx, id, req.PreferenceValue, claims.UserID)
err = handler.module.UpdateByOrg(ctx, valuer.MustNewUUID(claims.OrgID), name, req.Value)
if err != nil {
render.Error(rw, err)
return
@ -156,7 +111,7 @@ func (handler *handler) UpdateUser(rw http.ResponseWriter, r *http.Request) {
render.Success(rw, http.StatusNoContent, nil)
}
func (handler *handler) GetAllUser(rw http.ResponseWriter, r *http.Request) {
func (handler *handler) ListByUser(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
@ -166,7 +121,7 @@ func (handler *handler) GetAllUser(rw http.ResponseWriter, r *http.Request) {
return
}
preferences, err := handler.module.GetAllUser(ctx, claims.OrgID, claims.UserID)
preferences, err := handler.module.ListByUser(ctx, valuer.MustNewUUID(claims.UserID))
if err != nil {
render.Error(rw, err)
return
@ -174,3 +129,72 @@ func (handler *handler) GetAllUser(rw http.ResponseWriter, r *http.Request) {
render.Success(rw, http.StatusOK, preferences)
}
func (handler *handler) GetByUser(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
nameString, ok := mux.Vars(r)["preferenceId"]
if !ok {
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required"))
return
}
name, err := preferencetypes.NewName(nameString)
if err != nil {
render.Error(rw, err)
return
}
preference, err := handler.module.GetByUser(ctx, valuer.MustNewUUID(claims.UserID), name)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, preference)
}
func (handler *handler) UpdateByUser(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
nameString, ok := mux.Vars(r)["preferenceId"]
if !ok {
render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required"))
return
}
name, err := preferencetypes.NewName(nameString)
if err != nil {
render.Error(rw, err)
return
}
req := new(preferencetypes.UpdatablePreference)
err = json.NewDecoder(r.Body).Decode(req)
if err != nil {
render.Error(rw, err)
return
}
err = handler.module.UpdateByUser(ctx, valuer.MustNewUUID(claims.UserID), name, req.Value)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusNoContent, nil)
}

View File

@ -2,8 +2,6 @@ package implpreference
import (
"context"
"database/sql"
"encoding/json"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/modules/preference"
@ -13,266 +11,156 @@ import (
// Do not take inspiration from this code, it is a work in progress. See Organization module for a better implementation.
type module struct {
store preferencetypes.Store
defaultMap map[string]preferencetypes.Preference
store preferencetypes.Store
available map[preferencetypes.Name]preferencetypes.Preference
}
func NewModule(store preferencetypes.Store, defaultMap map[string]preferencetypes.Preference) preference.Module {
return &module{store: store, defaultMap: defaultMap}
func NewModule(store preferencetypes.Store, available map[preferencetypes.Name]preferencetypes.Preference) preference.Module {
return &module{store: store, available: available}
}
func (module *module) GetOrg(ctx context.Context, preferenceID string, orgID string) (*preferencetypes.GettablePreference, error) {
preference, seen := module.defaultMap[preferenceID]
if !seen {
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "cannot find preference with id: %s", preferenceID)
}
isEnabled := preference.IsEnabledForScope(preferencetypes.OrgAllowedScope)
if !isEnabled {
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "preference is not enabled at org scope: %s", preferenceID)
}
org, err := module.store.GetOrg(ctx, orgID, preferenceID)
func (module *module) ListByOrg(ctx context.Context, orgID valuer.UUID) ([]*preferencetypes.GettablePreference, error) {
storableOrgPreferences, err := module.store.ListByOrg(ctx, orgID)
if err != nil {
if err == sql.ErrNoRows {
return &preferencetypes.GettablePreference{
PreferenceID: preferenceID,
PreferenceValue: preference.DefaultValue,
}, nil
}
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "error in fetching the org preference: %s", preferenceID)
return nil, err
}
return &preferencetypes.GettablePreference{
PreferenceID: preferenceID,
PreferenceValue: preference.SanitizeValue(org.PreferenceValue),
}, nil
var gettablePreferences []*preferencetypes.GettablePreference
for _, preference := range module.available {
copyOfPreference, err := preferencetypes.NewPreferenceFromAvailable(preference.Name, module.available)
if err != nil {
continue
}
for _, storableOrgPreference := range storableOrgPreferences {
if storableOrgPreference.Name == preference.Name {
gettablePreferences = append(gettablePreferences, preferencetypes.NewGettablePreference(copyOfPreference, storableOrgPreference.Value))
continue
}
gettablePreferences = append(gettablePreferences, preferencetypes.NewGettablePreference(copyOfPreference, preference.DefaultValue))
}
}
return gettablePreferences, nil
}
func (module *module) UpdateOrg(ctx context.Context, preferenceID string, preferenceValue interface{}, orgID string) error {
preference, seen := module.defaultMap[preferenceID]
if !seen {
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "cannot find preference with id: %s", preferenceID)
func (module *module) GetByOrg(ctx context.Context, orgID valuer.UUID, name preferencetypes.Name) (*preferencetypes.GettablePreference, error) {
preference, err := preferencetypes.NewPreference(name, preferencetypes.ScopeOrg, module.available)
if err != nil {
return nil, err
}
isEnabled := preference.IsEnabledForScope(preferencetypes.OrgAllowedScope)
if !isEnabled {
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "preference is not enabled at org scope: %s", preferenceID)
org, err := module.store.GetByOrg(ctx, orgID, name)
if err != nil {
if errors.As(err, errors.TypeNotFound) {
return preferencetypes.NewGettablePreference(preference, preference.DefaultValue), nil
}
return nil, err
}
err := preference.IsValidValue(preferenceValue)
return preferencetypes.NewGettablePreference(preference, org.Value), nil
}
func (module *module) UpdateByOrg(ctx context.Context, orgID valuer.UUID, name preferencetypes.Name, preferenceValue string) error {
preference, err := preferencetypes.NewPreference(name, preferencetypes.ScopeOrg, module.available)
if err != nil {
return err
}
storableValue, encodeErr := json.Marshal(preferenceValue)
if encodeErr != nil {
return errors.Wrapf(encodeErr, errors.TypeInvalidInput, errors.CodeInvalidInput, "error in encoding the preference value")
_, err = preferencetypes.NewPreferenceValueFromString(preference, preferenceValue)
if err != nil {
return err
}
org, dberr := module.store.GetOrg(ctx, orgID, preferenceID)
if dberr != nil && dberr != sql.ErrNoRows {
return errors.Wrapf(dberr, errors.TypeInternal, errors.CodeInternal, "error in getting the preference value")
storableOrgPreference, err := module.store.GetByOrg(ctx, orgID, name)
if err != nil {
if !errors.As(err, errors.TypeNotFound) {
return err
}
}
if dberr != nil {
org.ID = valuer.GenerateUUID()
org.PreferenceID = preferenceID
org.PreferenceValue = string(storableValue)
org.OrgID = orgID
} else {
org.PreferenceValue = string(storableValue)
if storableOrgPreference == nil {
storableOrgPreference = preferencetypes.NewStorableOrgPreference(preference, preferenceValue, orgID)
}
dberr = module.store.UpsertOrg(ctx, org)
if dberr != nil {
return errors.Wrapf(dberr, errors.TypeInternal, errors.CodeInternal, "error in setting the preference value")
err = module.store.UpsertByOrg(ctx, storableOrgPreference)
if err != nil {
return err
}
return nil
}
func (module *module) GetAllOrg(ctx context.Context, orgID string) ([]*preferencetypes.PreferenceWithValue, error) {
allOrgs := []*preferencetypes.PreferenceWithValue{}
orgs, err := module.store.GetAllOrg(ctx, orgID)
func (module *module) ListByUser(ctx context.Context, userID valuer.UUID) ([]*preferencetypes.GettablePreference, error) {
storableUserPreferences, err := module.store.ListByUser(ctx, userID)
if err != nil {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "error in setting all org preference values")
return nil, err
}
preferenceValueMap := map[string]interface{}{}
for _, preferenceValue := range orgs {
preferenceValueMap[preferenceValue.PreferenceID] = preferenceValue.PreferenceValue
}
for _, preference := range module.defaultMap {
isEnabledForOrgScope := preference.IsEnabledForScope(preferencetypes.OrgAllowedScope)
if isEnabledForOrgScope {
preferenceWithValue := &preferencetypes.PreferenceWithValue{}
preferenceWithValue.Key = preference.Key
preferenceWithValue.Name = preference.Name
preferenceWithValue.Description = preference.Description
preferenceWithValue.AllowedScopes = preference.AllowedScopes
preferenceWithValue.AllowedValues = preference.AllowedValues
preferenceWithValue.DefaultValue = preference.DefaultValue
preferenceWithValue.Range = preference.Range
preferenceWithValue.ValueType = preference.ValueType
preferenceWithValue.IsDiscreteValues = preference.IsDiscreteValues
value, seen := preferenceValueMap[preference.Key]
if seen {
preferenceWithValue.Value = value
} else {
preferenceWithValue.Value = preference.DefaultValue
var gettablePreferences []*preferencetypes.GettablePreference
for _, preference := range module.available {
copyOfPreference, err := preferencetypes.NewPreferenceFromAvailable(preference.Name, module.available)
if err != nil {
continue
}
for _, storableUserPreference := range storableUserPreferences {
if storableUserPreference.Name == preference.Name {
gettablePreferences = append(gettablePreferences, preferencetypes.NewGettablePreference(copyOfPreference, storableUserPreference.Value))
continue
}
preferenceWithValue.Value = preference.SanitizeValue(preferenceWithValue.Value)
allOrgs = append(allOrgs, preferenceWithValue)
gettablePreferences = append(gettablePreferences, preferencetypes.NewGettablePreference(copyOfPreference, preference.DefaultValue))
}
}
return allOrgs, nil
return gettablePreferences, nil
}
func (module *module) GetUser(ctx context.Context, preferenceID string, orgID string, userID string) (*preferencetypes.GettablePreference, error) {
preference, seen := module.defaultMap[preferenceID]
if !seen {
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "cannot find preference with id: %s", preferenceID)
func (module *module) GetByUser(ctx context.Context, userID valuer.UUID, name preferencetypes.Name) (*preferencetypes.GettablePreference, error) {
preference, err := preferencetypes.NewPreference(name, preferencetypes.ScopeUser, module.available)
if err != nil {
return nil, err
}
preferenceValue := preferencetypes.GettablePreference{
PreferenceID: preferenceID,
PreferenceValue: preference.DefaultValue,
}
isEnabledAtUserScope := preference.IsEnabledForScope(preferencetypes.UserAllowedScope)
if !isEnabledAtUserScope {
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "preference is not enabled at user scope: %s", preferenceID)
}
isEnabledAtOrgScope := preference.IsEnabledForScope(preferencetypes.OrgAllowedScope)
if isEnabledAtOrgScope {
org, err := module.store.GetOrg(ctx, orgID, preferenceID)
if err != nil && err != sql.ErrNoRows {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "error in fetching the org preference: %s", preferenceID)
}
if err == nil {
preferenceValue.PreferenceValue = org.PreferenceValue
storableUserPreference, err := module.store.GetByUser(ctx, userID, name)
if err != nil {
if errors.As(err, errors.TypeNotFound) {
return preferencetypes.NewGettablePreference(preference, preference.DefaultValue), nil
}
return nil, err
}
user, err := module.store.GetUser(ctx, userID, preferenceID)
if err != nil && err != sql.ErrNoRows {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "error in fetching the user preference: %s", preferenceID)
}
if err == nil {
preferenceValue.PreferenceValue = user.PreferenceValue
}
return &preferencetypes.GettablePreference{
PreferenceID: preferenceValue.PreferenceID,
PreferenceValue: preference.SanitizeValue(preferenceValue.PreferenceValue),
}, nil
return preferencetypes.NewGettablePreference(preference, storableUserPreference.Value), nil
}
func (module *module) UpdateUser(ctx context.Context, preferenceID string, preferenceValue interface{}, userID string) error {
preference, seen := module.defaultMap[preferenceID]
if !seen {
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "cannot find preference with id: %s", preferenceID)
}
isEnabledAtUserScope := preference.IsEnabledForScope(preferencetypes.UserAllowedScope)
if !isEnabledAtUserScope {
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "preference is not enabled at user scope: %s", preferenceID)
}
err := preference.IsValidValue(preferenceValue)
func (module *module) UpdateByUser(ctx context.Context, userID valuer.UUID, name preferencetypes.Name, preferenceValue string) error {
preference, err := preferencetypes.NewPreference(name, preferencetypes.ScopeUser, module.available)
if err != nil {
return err
}
storableValue, encodeErr := json.Marshal(preferenceValue)
if encodeErr != nil {
return errors.Wrapf(encodeErr, errors.TypeInvalidInput, errors.CodeInvalidInput, "error in encoding the preference value")
_, err = preferencetypes.NewPreferenceValueFromString(preference, preferenceValue)
if err != nil {
return err
}
user, dberr := module.store.GetUser(ctx, userID, preferenceID)
if dberr != nil && dberr != sql.ErrNoRows {
return errors.Wrapf(dberr, errors.TypeInternal, errors.CodeInternal, "error in getting the preference value")
storableUserPreference, err := module.store.GetByUser(ctx, userID, name)
if err != nil {
if !errors.As(err, errors.TypeNotFound) {
return err
}
}
if dberr != nil {
user.ID = valuer.GenerateUUID()
user.PreferenceID = preferenceID
user.PreferenceValue = string(storableValue)
user.UserID = userID
} else {
user.PreferenceValue = string(storableValue)
if storableUserPreference == nil {
storableUserPreference = preferencetypes.NewStorableUserPreference(preference, preferenceValue, userID)
}
dberr = module.store.UpsertUser(ctx, user)
if dberr != nil {
return errors.Wrapf(dberr, errors.TypeInternal, errors.CodeInternal, "error in setting the preference value")
err = module.store.UpsertByUser(ctx, storableUserPreference)
if err != nil {
return err
}
return nil
}
func (module *module) GetAllUser(ctx context.Context, orgID string, userID string) ([]*preferencetypes.PreferenceWithValue, error) {
allUsers := []*preferencetypes.PreferenceWithValue{}
orgs, err := module.store.GetAllOrg(ctx, orgID)
if err != nil {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "error in setting all org preference values")
}
preferenceOrgValueMap := map[string]interface{}{}
for _, preferenceValue := range orgs {
preferenceOrgValueMap[preferenceValue.PreferenceID] = preferenceValue.PreferenceValue
}
users, err := module.store.GetAllUser(ctx, userID)
if err != nil {
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "error in setting all user preference values")
}
preferenceUserValueMap := map[string]interface{}{}
for _, preferenceValue := range users {
preferenceUserValueMap[preferenceValue.PreferenceID] = preferenceValue.PreferenceValue
}
for _, preference := range module.defaultMap {
isEnabledForUserScope := preference.IsEnabledForScope(preferencetypes.UserAllowedScope)
if isEnabledForUserScope {
preferenceWithValue := &preferencetypes.PreferenceWithValue{}
preferenceWithValue.Key = preference.Key
preferenceWithValue.Name = preference.Name
preferenceWithValue.Description = preference.Description
preferenceWithValue.AllowedScopes = preference.AllowedScopes
preferenceWithValue.AllowedValues = preference.AllowedValues
preferenceWithValue.DefaultValue = preference.DefaultValue
preferenceWithValue.Range = preference.Range
preferenceWithValue.ValueType = preference.ValueType
preferenceWithValue.IsDiscreteValues = preference.IsDiscreteValues
preferenceWithValue.Value = preference.DefaultValue
isEnabledForOrgScope := preference.IsEnabledForScope(preferencetypes.OrgAllowedScope)
if isEnabledForOrgScope {
value, seen := preferenceOrgValueMap[preference.Key]
if seen {
preferenceWithValue.Value = value
}
}
value, seen := preferenceUserValueMap[preference.Key]
if seen {
preferenceWithValue.Value = value
}
preferenceWithValue.Value = preference.SanitizeValue(preferenceWithValue.Value)
allUsers = append(allUsers, preferenceWithValue)
}
}
return allUsers, nil
}

View File

@ -5,6 +5,7 @@ import (
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/types/preferencetypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type store struct {
@ -15,14 +16,14 @@ func NewStore(db sqlstore.SQLStore) preferencetypes.Store {
return &store{store: db}
}
func (store *store) GetOrg(ctx context.Context, orgID string, preferenceID string) (*preferencetypes.StorableOrgPreference, error) {
func (store *store) GetByOrg(ctx context.Context, orgID valuer.UUID, name preferencetypes.Name) (*preferencetypes.StorableOrgPreference, error) {
orgPreference := new(preferencetypes.StorableOrgPreference)
err := store.
store.
BunDB().
NewSelect().
Model(orgPreference).
Where("preference_id = ?", preferenceID).
Where("preference_id = ?", name).
Where("org_id = ?", orgID).
Scan(ctx)
@ -33,7 +34,7 @@ func (store *store) GetOrg(ctx context.Context, orgID string, preferenceID strin
return orgPreference, nil
}
func (store *store) GetAllOrg(ctx context.Context, orgID string) ([]*preferencetypes.StorableOrgPreference, error) {
func (store *store) ListByOrg(ctx context.Context, orgID valuer.UUID) ([]*preferencetypes.StorableOrgPreference, error) {
orgPreferences := make([]*preferencetypes.StorableOrgPreference, 0)
err := store.
store.
@ -50,7 +51,7 @@ func (store *store) GetAllOrg(ctx context.Context, orgID string) ([]*preferencet
return orgPreferences, nil
}
func (store *store) UpsertOrg(ctx context.Context, orgPreference *preferencetypes.StorableOrgPreference) error {
func (store *store) UpsertByOrg(ctx context.Context, orgPreference *preferencetypes.StorableOrgPreference) error {
_, err := store.
store.
BunDB().
@ -65,17 +66,16 @@ func (store *store) UpsertOrg(ctx context.Context, orgPreference *preferencetype
return nil
}
func (store *store) GetUser(ctx context.Context, userID string, preferenceID string) (*preferencetypes.StorableUserPreference, error) {
func (store *store) GetByUser(ctx context.Context, userID valuer.UUID, name preferencetypes.Name) (*preferencetypes.StorableUserPreference, error) {
userPreference := new(preferencetypes.StorableUserPreference)
err := store.
store.
BunDB().
NewSelect().
Model(userPreference).
Where("preference_id = ?", preferenceID).
Where("user_id = ?", userID).
Scan(ctx)
if err != nil {
return userPreference, err
}
@ -83,7 +83,7 @@ func (store *store) GetUser(ctx context.Context, userID string, preferenceID str
return userPreference, nil
}
func (store *store) GetAllUser(ctx context.Context, userID string) ([]*preferencetypes.StorableUserPreference, error) {
func (store *store) ListByUser(ctx context.Context, userID valuer.UUID) ([]*preferencetypes.StorableUserPreference, error) {
userPreferences := make([]*preferencetypes.StorableUserPreference, 0)
err := store.
store.
@ -100,7 +100,7 @@ func (store *store) GetAllUser(ctx context.Context, userID string) ([]*preferenc
return userPreferences, nil
}
func (store *store) UpsertUser(ctx context.Context, userPreference *preferencetypes.StorableUserPreference) error {
func (store *store) UpsertByUser(ctx context.Context, userPreference *preferencetypes.StorableUserPreference) error {
_, err := store.
store.
BunDB().

View File

@ -5,44 +5,45 @@ import (
"net/http"
"github.com/SigNoz/signoz/pkg/types/preferencetypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type Module interface {
// Returns the preference for the given organization
GetOrg(ctx context.Context, preferenceId string, orgId string) (*preferencetypes.GettablePreference, error)
// Returns the preference for the given user
GetUser(ctx context.Context, preferenceId string, orgId string, userId string) (*preferencetypes.GettablePreference, error)
// Returns all preferences for the given organization
GetAllOrg(ctx context.Context, orgId string) ([]*preferencetypes.PreferenceWithValue, error)
ListByOrg(context.Context, valuer.UUID) ([]*preferencetypes.GettablePreference, error)
// Returns all preferences for the given user
GetAllUser(ctx context.Context, orgId string, userId string) ([]*preferencetypes.PreferenceWithValue, error)
// Returns the preference for the given organization by name.
GetByOrg(context.Context, valuer.UUID, preferencetypes.Name) (*preferencetypes.GettablePreference, error)
// Updates the preference for the given organization
UpdateOrg(ctx context.Context, preferenceId string, preferenceValue interface{}, orgId string) error
UpdateByOrg(context.Context, valuer.UUID, preferencetypes.Name, string) error
// Returns all preferences for the given user
ListByUser(context.Context, valuer.UUID) ([]*preferencetypes.GettablePreference, error)
// Returns the preference for the given user by name.
GetByUser(context.Context, valuer.UUID, preferencetypes.Name) (*preferencetypes.GettablePreference, error)
// Updates the preference for the given user
UpdateUser(ctx context.Context, preferenceId string, preferenceValue interface{}, userId string) error
UpdateByUser(context.Context, valuer.UUID, preferencetypes.Name, string) error
}
type Handler interface {
// Returns the preference for the given organization
GetOrg(http.ResponseWriter, *http.Request)
GetByOrg(http.ResponseWriter, *http.Request)
// Updates the preference for the given organization
UpdateOrg(http.ResponseWriter, *http.Request)
UpdateByOrg(http.ResponseWriter, *http.Request)
// Returns all preferences for the given organization
GetAllOrg(http.ResponseWriter, *http.Request)
ListByOrg(http.ResponseWriter, *http.Request)
// Returns the preference for the given user
GetUser(http.ResponseWriter, *http.Request)
GetByUser(http.ResponseWriter, *http.Request)
// Updates the preference for the given user
UpdateUser(http.ResponseWriter, *http.Request)
UpdateByUser(http.ResponseWriter, *http.Request)
// Returns all preferences for the given user
GetAllUser(http.ResponseWriter, *http.Request)
ListByUser(http.ResponseWriter, *http.Request)
}

View File

@ -561,12 +561,12 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *middleware.AuthZ) {
router.HandleFunc("/api/v1/disks", am.ViewAccess(aH.getDisks)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/user/preferences", am.ViewAccess(aH.Signoz.Handlers.Preference.GetAllUser)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/user/preferences/{preferenceId}", am.ViewAccess(aH.Signoz.Handlers.Preference.GetUser)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/user/preferences/{preferenceId}", am.ViewAccess(aH.Signoz.Handlers.Preference.UpdateUser)).Methods(http.MethodPut)
router.HandleFunc("/api/v1/org/preferences", am.AdminAccess(aH.Signoz.Handlers.Preference.GetAllOrg)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/org/preferences/{preferenceId}", am.AdminAccess(aH.Signoz.Handlers.Preference.GetOrg)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/org/preferences/{preferenceId}", am.AdminAccess(aH.Signoz.Handlers.Preference.UpdateOrg)).Methods(http.MethodPut)
router.HandleFunc("/api/v1/user/preferences", am.ViewAccess(aH.Signoz.Handlers.Preference.ListByUser)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/user/preferences/{preferenceId}", am.ViewAccess(aH.Signoz.Handlers.Preference.GetByUser)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/user/preferences/{preferenceId}", am.ViewAccess(aH.Signoz.Handlers.Preference.UpdateByUser)).Methods(http.MethodPut)
router.HandleFunc("/api/v1/org/preferences", am.AdminAccess(aH.Signoz.Handlers.Preference.ListByOrg)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/org/preferences/{preferenceId}", am.AdminAccess(aH.Signoz.Handlers.Preference.GetByOrg)).Methods(http.MethodGet)
router.HandleFunc("/api/v1/org/preferences/{preferenceId}", am.AdminAccess(aH.Signoz.Handlers.Preference.UpdateByOrg)).Methods(http.MethodPut)
// Quick Filters
router.HandleFunc("/api/v1/orgs/me/filters", am.ViewAccess(aH.Signoz.Handlers.QuickFilter.GetQuickFilters)).Methods(http.MethodGet)

View File

@ -34,7 +34,7 @@ type Modules struct {
Apdex apdex.Module
Dashboard dashboard.Module
QuickFilter quickfilter.Module
TraceFunnel tracefunnel.Module
TraceFunnel tracefunnel.Module
}
func NewModules(
@ -51,12 +51,12 @@ func NewModules(
return Modules{
OrgGetter: orgGetter,
OrgSetter: orgSetter,
Preference: implpreference.NewModule(implpreference.NewStore(sqlstore), preferencetypes.NewDefaultPreferenceMap()),
Preference: implpreference.NewModule(implpreference.NewStore(sqlstore), preferencetypes.NewAvailablePreference()),
SavedView: implsavedview.NewModule(sqlstore),
Apdex: implapdex.NewModule(sqlstore),
Dashboard: impldashboard.NewModule(sqlstore, providerSettings),
User: user,
QuickFilter: quickfilter,
TraceFunnel: impltracefunnel.NewModule(impltracefunnel.NewStore(sqlstore)),
TraceFunnel: impltracefunnel.NewModule(impltracefunnel.NewStore(sqlstore)),
}
}

View File

@ -0,0 +1,44 @@
package preferencetypes
import (
"slices"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/valuer"
)
var (
NameOrgOnboarding = Name{valuer.NewString("org_onboarding")}
NameWelcomeChecklistDoLater = Name{valuer.NewString("welcome_checklist_do_later")}
NameWelcomeChecklistSendLogsSkipped = Name{valuer.NewString("welcome_checklist_send_logs_skipped")}
NameWelcomeChecklistSendTracesSkipped = Name{valuer.NewString("welcome_checklist_send_traces_skipped")}
NameWelcomeChecklistSendInfraMetricsSkipped = Name{valuer.NewString("welcome_checklist_send_infra_metrics_skipped")}
NameWelcomeChecklistSetupDashboardsSkipped = Name{valuer.NewString("welcome_checklist_setup_dashboards_skipped")}
NameWelcomeChecklistSetupAlertsSkipped = Name{valuer.NewString("welcome_checklist_setup_alerts_skipped")}
NameWelcomeChecklistSetupSavedViewSkipped = Name{valuer.NewString("welcome_checklist_setup_saved_view_skipped")}
NameSidenavPinned = Name{valuer.NewString("sidenav_pinned")}
)
type Name struct{ valuer.String }
func NewName(name string) (Name, error) {
ok := slices.Contains(
[]string{
NameOrgOnboarding.StringValue(),
NameWelcomeChecklistDoLater.StringValue(),
NameWelcomeChecklistSendLogsSkipped.StringValue(),
NameWelcomeChecklistSendTracesSkipped.StringValue(),
NameWelcomeChecklistSendInfraMetricsSkipped.StringValue(),
NameWelcomeChecklistSetupDashboardsSkipped.StringValue(),
NameWelcomeChecklistSetupAlertsSkipped.StringValue(),
NameWelcomeChecklistSetupSavedViewSkipped.StringValue(),
NameSidenavPinned.StringValue(),
},
name,
)
if !ok {
return Name{}, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "invalid name: %s", name)
}
return Name{valuer.NewString(name)}, nil
}

View File

@ -2,298 +2,204 @@ package preferencetypes
import (
"context"
"strings"
"slices"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/uptrace/bun"
)
type GettablePreference struct {
PreferenceID string `json:"preference_id" db:"preference_id"`
PreferenceValue interface{} `json:"preference_value" db:"preference_value"`
}
type UpdatablePreference struct {
PreferenceValue interface{} `json:"preference_value" db:"preference_value"`
type Preference struct {
Name Name `json:"name"`
Description string `json:"description"`
ValueType ValueType `json:"valueType"`
DefaultValue any `json:"defaultValue"`
AllowedValues []any `json:"allowedValues"`
IsDiscreteValues bool `json:"isDiscreteValues"`
Range Range `json:"range"`
AllowedScopes []Scope `json:"allowedScopes"`
}
type StorableOrgPreference struct {
bun.BaseModel `bun:"table:org_preference"`
types.Identifiable
PreferenceID string `bun:"preference_id,type:text,notnull"`
PreferenceValue string `bun:"preference_value,type:text,notnull"`
OrgID string `bun:"org_id,type:text,notnull"`
Name Name `bun:"preference_id,type:text,notnull"`
Value string `bun:"preference_value,type:text,notnull"`
OrgID valuer.UUID `bun:"org_id,type:text,notnull"`
}
type StorableUserPreference struct {
bun.BaseModel `bun:"table:user_preference"`
types.Identifiable
PreferenceID string `bun:"preference_id,type:text,notnull"`
PreferenceValue string `bun:"preference_value,type:text,notnull"`
UserID string `bun:"user_id,type:text,notnull"`
Name Name `bun:"preference_id,type:text,notnull"`
Value string `bun:"preference_value,type:text,notnull"`
UserID valuer.UUID `bun:"user_id,type:text,notnull"`
}
type Preference struct {
Key string `json:"key"`
Name string `json:"name"`
Description string `json:"description"`
ValueType string `json:"valueType"`
DefaultValue interface{} `json:"defaultValue"`
AllowedValues []interface{} `json:"allowedValues"`
IsDiscreteValues bool `json:"isDiscreteValues"`
Range Range `json:"range"`
AllowedScopes []string `json:"allowedScopes"`
type GettablePreference struct {
*Preference
Value any `json:"preference_value"`
}
func NewDefaultPreferenceMap() map[string]Preference {
return map[string]Preference{
"ORG_ONBOARDING": {
Key: "ORG_ONBOARDING",
Name: "Organisation Onboarding",
type UpdatablePreference struct {
Value string `json:"preference_value"`
}
func NewAvailablePreference() map[Name]Preference {
return map[Name]Preference{
NameOrgOnboarding: {
Name: NameOrgOnboarding,
Description: "Organisation Onboarding",
ValueType: "boolean",
ValueType: ValueTypeBoolean,
DefaultValue: false,
AllowedValues: []interface{}{true, false},
AllowedValues: []any{true, false},
IsDiscreteValues: true,
AllowedScopes: []string{"org"},
AllowedScopes: []Scope{ScopeOrg},
},
"WELCOME_CHECKLIST_DO_LATER": {
Key: "WELCOME_CHECKLIST_DO_LATER",
Name: "Welcome Checklist Do Later",
NameWelcomeChecklistDoLater: {
Name: NameWelcomeChecklistDoLater,
Description: "Welcome Checklist Do Later",
ValueType: "boolean",
ValueType: ValueTypeBoolean,
DefaultValue: false,
AllowedValues: []interface{}{true, false},
AllowedValues: []any{true, false},
IsDiscreteValues: true,
AllowedScopes: []string{"user"},
AllowedScopes: []Scope{ScopeUser},
},
"WELCOME_CHECKLIST_SEND_LOGS_SKIPPED": {
Key: "WELCOME_CHECKLIST_SEND_LOGS_SKIPPED",
Name: "Welcome Checklist Send Logs Skipped",
NameWelcomeChecklistSendLogsSkipped: {
Name: NameWelcomeChecklistSendLogsSkipped,
Description: "Welcome Checklist Send Logs Skipped",
ValueType: "boolean",
ValueType: ValueTypeBoolean,
DefaultValue: false,
AllowedValues: []interface{}{true, false},
AllowedValues: []any{true, false},
IsDiscreteValues: true,
AllowedScopes: []string{"user"},
AllowedScopes: []Scope{ScopeUser},
},
"WELCOME_CHECKLIST_SEND_TRACES_SKIPPED": {
Key: "WELCOME_CHECKLIST_SEND_TRACES_SKIPPED",
Name: "Welcome Checklist Send Traces Skipped",
NameWelcomeChecklistSendTracesSkipped: {
Name: NameWelcomeChecklistSendTracesSkipped,
Description: "Welcome Checklist Send Traces Skipped",
ValueType: "boolean",
ValueType: ValueTypeBoolean,
DefaultValue: false,
AllowedValues: []interface{}{true, false},
AllowedValues: []any{true, false},
IsDiscreteValues: true,
AllowedScopes: []string{"user"},
AllowedScopes: []Scope{ScopeUser},
},
"WELCOME_CHECKLIST_SEND_INFRA_METRICS_SKIPPED": {
Key: "WELCOME_CHECKLIST_SEND_INFRA_METRICS_SKIPPED",
Name: "Welcome Checklist Send Infra Metrics Skipped",
NameWelcomeChecklistSendInfraMetricsSkipped: {
Name: NameWelcomeChecklistSendInfraMetricsSkipped,
Description: "Welcome Checklist Send Infra Metrics Skipped",
ValueType: "boolean",
ValueType: ValueTypeBoolean,
DefaultValue: false,
AllowedValues: []interface{}{true, false},
AllowedValues: []any{true, false},
IsDiscreteValues: true,
AllowedScopes: []string{"user"},
AllowedScopes: []Scope{ScopeUser},
},
"WELCOME_CHECKLIST_SETUP_DASHBOARDS_SKIPPED": {
Key: "WELCOME_CHECKLIST_SETUP_DASHBOARDS_SKIPPED",
Name: "Welcome Checklist Setup Dashboards Skipped",
NameWelcomeChecklistSetupDashboardsSkipped: {
Name: NameWelcomeChecklistSetupDashboardsSkipped,
Description: "Welcome Checklist Setup Dashboards Skipped",
ValueType: "boolean",
ValueType: ValueTypeBoolean,
DefaultValue: false,
AllowedValues: []interface{}{true, false},
AllowedValues: []any{true, false},
IsDiscreteValues: true,
AllowedScopes: []string{"user"},
AllowedScopes: []Scope{ScopeUser},
},
"WELCOME_CHECKLIST_SETUP_ALERTS_SKIPPED": {
Key: "WELCOME_CHECKLIST_SETUP_ALERTS_SKIPPED",
Name: "Welcome Checklist Setup Alerts Skipped",
NameWelcomeChecklistSetupAlertsSkipped: {
Name: NameWelcomeChecklistSetupAlertsSkipped,
Description: "Welcome Checklist Setup Alerts Skipped",
ValueType: "boolean",
ValueType: ValueTypeBoolean,
DefaultValue: false,
AllowedValues: []interface{}{true, false},
AllowedValues: []any{true, false},
IsDiscreteValues: true,
AllowedScopes: []string{"user"},
AllowedScopes: []Scope{ScopeUser},
},
"WELCOME_CHECKLIST_SETUP_SAVED_VIEW_SKIPPED": {
Key: "WELCOME_CHECKLIST_SETUP_SAVED_VIEW_SKIPPED",
Name: "Welcome Checklist Setup Saved View Skipped",
NameWelcomeChecklistSetupSavedViewSkipped: {
Name: NameWelcomeChecklistSetupSavedViewSkipped,
Description: "Welcome Checklist Setup Saved View Skipped",
ValueType: "boolean",
ValueType: ValueTypeBoolean,
DefaultValue: false,
AllowedValues: []interface{}{true, false},
AllowedValues: []any{true, false},
IsDiscreteValues: true,
AllowedScopes: []string{"user"},
AllowedScopes: []Scope{ScopeUser},
},
"SIDENAV_PINNED": {
Key: "SIDENAV_PINNED",
Name: "Keep the primary sidenav always open",
NameSidenavPinned: {
Name: NameSidenavPinned,
Description: "Controls whether the primary sidenav remains expanded or can be collapsed. When enabled, the sidenav will stay open and pinned to provide constant visibility of navigation options.",
ValueType: "boolean",
ValueType: ValueTypeBoolean,
DefaultValue: false,
AllowedValues: []interface{}{true, false},
AllowedValues: []any{true, false},
IsDiscreteValues: true,
AllowedScopes: []string{"user"},
AllowedScopes: []Scope{ScopeUser},
},
}
}
func (p *Preference) ErrorValueTypeMismatch() error {
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "the preference value is not of expected type: %s", p.ValueType)
func NewPreference(name Name, scope Scope, available map[Name]Preference) (*Preference, error) {
preference, ok := available[name]
if !ok {
return nil, errors.Newf(errors.TypeNotFound, errors.CodeNotFound, "the preference does not exist: %s", name)
}
if !slices.Contains(preference.AllowedScopes, scope) {
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "the preference is not allowed for the given scope: %s", scope)
}
return &Preference{
Name: name,
Description: preference.Description,
ValueType: preference.ValueType,
DefaultValue: preference.DefaultValue,
AllowedValues: preference.AllowedValues,
IsDiscreteValues: preference.IsDiscreteValues,
Range: preference.Range,
AllowedScopes: preference.AllowedScopes,
}, nil
}
func (p *Preference) checkIfInAllowedValues(preferenceValue interface{}) (bool, error) {
switch p.ValueType {
case PreferenceValueTypeInteger:
_, ok := preferenceValue.(int64)
if !ok {
return false, p.ErrorValueTypeMismatch()
}
case PreferenceValueTypeFloat:
_, ok := preferenceValue.(float64)
if !ok {
return false, p.ErrorValueTypeMismatch()
}
case PreferenceValueTypeString:
_, ok := preferenceValue.(string)
if !ok {
return false, p.ErrorValueTypeMismatch()
}
case PreferenceValueTypeBoolean:
_, ok := preferenceValue.(bool)
if !ok {
return false, p.ErrorValueTypeMismatch()
}
func NewPreferenceFromAvailable(name Name, available map[Name]Preference) (*Preference, error) {
preference, ok := available[name]
if !ok {
return nil, errors.Newf(errors.TypeNotFound, errors.CodeNotFound, "the preference does not exist: %s", name)
}
isInAllowedValues := false
for _, value := range p.AllowedValues {
switch p.ValueType {
case PreferenceValueTypeInteger:
allowedValue, ok := value.(int64)
if !ok {
return false, p.ErrorValueTypeMismatch()
}
if allowedValue == preferenceValue {
isInAllowedValues = true
}
case PreferenceValueTypeFloat:
allowedValue, ok := value.(float64)
if !ok {
return false, p.ErrorValueTypeMismatch()
}
if allowedValue == preferenceValue {
isInAllowedValues = true
}
case PreferenceValueTypeString:
allowedValue, ok := value.(string)
if !ok {
return false, p.ErrorValueTypeMismatch()
}
if allowedValue == preferenceValue {
isInAllowedValues = true
}
case PreferenceValueTypeBoolean:
allowedValue, ok := value.(bool)
if !ok {
return false, p.ErrorValueTypeMismatch()
}
if allowedValue == preferenceValue {
isInAllowedValues = true
}
}
}
return isInAllowedValues, nil
return &Preference{
Name: name,
Description: preference.Description,
ValueType: preference.ValueType,
DefaultValue: preference.DefaultValue,
AllowedValues: preference.AllowedValues,
IsDiscreteValues: preference.IsDiscreteValues,
Range: preference.Range,
AllowedScopes: preference.AllowedScopes,
}, nil
}
func (p *Preference) IsValidValue(preferenceValue interface{}) error {
typeSafeValue := preferenceValue
switch p.ValueType {
case PreferenceValueTypeInteger:
val, ok := preferenceValue.(int64)
if !ok {
floatVal, ok := preferenceValue.(float64)
if !ok || floatVal != float64(int64(floatVal)) {
return p.ErrorValueTypeMismatch()
}
val = int64(floatVal)
typeSafeValue = val
}
if !p.IsDiscreteValues {
if val < p.Range.Min || val > p.Range.Max {
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "the preference value is not in the range specified, min: %v , max: %v", p.Range.Min, p.Range.Max)
}
}
case PreferenceValueTypeString:
_, ok := preferenceValue.(string)
if !ok {
return p.ErrorValueTypeMismatch()
}
case PreferenceValueTypeFloat:
_, ok := preferenceValue.(float64)
if !ok {
return p.ErrorValueTypeMismatch()
}
case PreferenceValueTypeBoolean:
_, ok := preferenceValue.(bool)
if !ok {
return p.ErrorValueTypeMismatch()
}
func NewGettablePreference(preference *Preference, value any) *GettablePreference {
return &GettablePreference{
Preference: preference,
Value: value,
}
// check the validity of the value being part of allowed values or the range specified if any
if p.IsDiscreteValues {
if p.AllowedValues != nil {
isInAllowedValues, valueMisMatchErr := p.checkIfInAllowedValues(typeSafeValue)
if valueMisMatchErr != nil {
return valueMisMatchErr
}
if !isInAllowedValues {
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "the preference value is not in the list of allowedValues: %v", p.AllowedValues)
}
}
}
return nil
}
func (p *Preference) IsEnabledForScope(scope string) bool {
isPreferenceEnabledForGivenScope := false
if p.AllowedScopes != nil {
for _, allowedScope := range p.AllowedScopes {
if allowedScope == strings.ToLower(scope) {
isPreferenceEnabledForGivenScope = true
}
}
func NewStorableOrgPreference(preference *Preference, value string, orgID valuer.UUID) *StorableOrgPreference {
return &StorableOrgPreference{
Name: preference.Name,
Value: value,
OrgID: orgID,
}
return isPreferenceEnabledForGivenScope
}
func (p *Preference) SanitizeValue(preferenceValue interface{}) interface{} {
switch p.ValueType {
case PreferenceValueTypeBoolean:
if preferenceValue == "1" || preferenceValue == true || preferenceValue == "true" {
return true
} else {
return false
}
default:
return preferenceValue
func NewStorableUserPreference(preference *Preference, value string, userID valuer.UUID) *StorableUserPreference {
return &StorableUserPreference{
Name: preference.Name,
Value: value,
UserID: userID,
}
}
type Store interface {
GetOrg(context.Context, string, string) (*StorableOrgPreference, error)
GetAllOrg(context.Context, string) ([]*StorableOrgPreference, error)
UpsertOrg(context.Context, *StorableOrgPreference) error
GetUser(context.Context, string, string) (*StorableUserPreference, error)
GetAllUser(context.Context, string) ([]*StorableUserPreference, error)
UpsertUser(context.Context, *StorableUserPreference) error
GetByOrg(context.Context, valuer.UUID, Name) (*StorableOrgPreference, error)
ListByOrg(context.Context, valuer.UUID) ([]*StorableOrgPreference, error)
UpsertByOrg(context.Context, *StorableOrgPreference) error
GetByUser(context.Context, valuer.UUID, Name) (*StorableUserPreference, error)
ListByUser(context.Context, valuer.UUID) ([]*StorableUserPreference, error)
UpsertByUser(context.Context, *StorableUserPreference) error
}

View File

@ -0,0 +1,10 @@
package preferencetypes
import "github.com/SigNoz/signoz/pkg/valuer"
var (
ScopeOrg = Scope{valuer.NewString("org")}
ScopeUser = Scope{valuer.NewString("user")}
)
type Scope struct{ valuer.String }

View File

@ -1,23 +1,97 @@
package preferencetypes
const (
PreferenceValueTypeInteger string = "integer"
PreferenceValueTypeFloat string = "float"
PreferenceValueTypeString string = "string"
PreferenceValueTypeBoolean string = "boolean"
import (
"encoding/json"
"strconv"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/valuer"
)
const (
OrgAllowedScope string = "org"
UserAllowedScope string = "user"
var (
ValueTypeInteger = ValueType{valuer.NewString("integer")}
ValueTypeFloat = ValueType{valuer.NewString("float")}
ValueTypeString = ValueType{valuer.NewString("string")}
ValueTypeBoolean = ValueType{valuer.NewString("boolean")}
ValueTypeArray = ValueType{valuer.NewString("array")}
ValueTypeObject = ValueType{valuer.NewString("object")}
)
type ValueType struct{ valuer.String }
type Range struct {
Min int64 `json:"min"`
Max int64 `json:"max"`
}
type PreferenceWithValue struct {
Preference
Value interface{} `json:"value"`
func NewPreferenceValueFromString(preference *Preference, value string) (any, error) {
switch preference.ValueType {
case ValueTypeInteger:
val, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return nil, err
}
if !preference.IsDiscreteValues {
if val < preference.Range.Min || val > preference.Range.Max {
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "value is not in the range specified, min: %d , max: %d", preference.Range.Min, preference.Range.Max)
}
}
if len(preference.AllowedValues) > 0 {
for _, allowedValue := range preference.AllowedValues {
if allowedValue == val {
return val, nil
}
}
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "value is not one of the allowed values: %v", preference.AllowedValues)
}
return val, nil
case ValueTypeFloat:
val, err := strconv.ParseFloat(value, 64)
if err != nil {
return nil, err
}
if len(preference.AllowedValues) > 0 {
for _, allowedValue := range preference.AllowedValues {
if allowedValue == val {
return val, nil
}
}
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "value is not one of the allowed values: %v", preference.AllowedValues)
}
return val, nil
case ValueTypeString:
if len(preference.AllowedValues) > 0 {
for _, allowedValue := range preference.AllowedValues {
if allowedValue == value {
return value, nil
}
}
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "value is not in the list of allowedValues: %v", preference.AllowedValues)
}
return value, nil
case ValueTypeBoolean:
return strconv.ParseBool(value)
case ValueTypeArray:
var arr []any
err := json.Unmarshal([]byte(value), &arr)
if err != nil {
return nil, err
}
return arr, nil
case ValueTypeObject:
var obj map[string]any
err := json.Unmarshal([]byte(value), &obj)
if err != nil {
return nil, err
}
return obj, nil
default:
return nil, errors.Newf(errors.TypeUnsupported, errors.CodeUnsupported, "the preference value type is not supported: %s", preference.ValueType)
}
}