mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 01:48:59 +08:00
feat(preference): multi tenant preference module (#7516)
* feat(sqlmigration): update the alertmanager tables * feat(sqlmigration): update the alertmanager tables * feat(sqlmigration): make the preference package multi tenant * feat(preference): address nit pick comments * feat(preference): added the cascade delete for preferences
This commit is contained in:
parent
b89ce82e25
commit
79e9d1b357
@ -11,6 +11,8 @@ import (
|
|||||||
"github.com/SigNoz/signoz/ee/query-service/license"
|
"github.com/SigNoz/signoz/ee/query-service/license"
|
||||||
"github.com/SigNoz/signoz/ee/query-service/usage"
|
"github.com/SigNoz/signoz/ee/query-service/usage"
|
||||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
|
preferencecore "github.com/SigNoz/signoz/pkg/modules/preference/core"
|
||||||
baseapp "github.com/SigNoz/signoz/pkg/query-service/app"
|
baseapp "github.com/SigNoz/signoz/pkg/query-service/app"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
|
"github.com/SigNoz/signoz/pkg/query-service/app/cloudintegrations"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
|
"github.com/SigNoz/signoz/pkg/query-service/app/integrations"
|
||||||
@ -21,6 +23,7 @@ import (
|
|||||||
rules "github.com/SigNoz/signoz/pkg/query-service/rules"
|
rules "github.com/SigNoz/signoz/pkg/query-service/rules"
|
||||||
"github.com/SigNoz/signoz/pkg/signoz"
|
"github.com/SigNoz/signoz/pkg/signoz"
|
||||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/preferencetypes"
|
||||||
"github.com/SigNoz/signoz/pkg/version"
|
"github.com/SigNoz/signoz/pkg/version"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
@ -54,6 +57,7 @@ type APIHandler struct {
|
|||||||
|
|
||||||
// NewAPIHandler returns an APIHandler
|
// NewAPIHandler returns an APIHandler
|
||||||
func NewAPIHandler(opts APIHandlerOptions, signoz *signoz.SigNoz) (*APIHandler, error) {
|
func NewAPIHandler(opts APIHandlerOptions, signoz *signoz.SigNoz) (*APIHandler, error) {
|
||||||
|
preference := preference.NewAPI(preferencecore.NewPreference(preferencecore.NewStore(signoz.SQLStore), preferencetypes.NewDefaultPreferenceMap()))
|
||||||
|
|
||||||
baseHandler, err := baseapp.NewAPIHandler(baseapp.APIHandlerOpts{
|
baseHandler, err := baseapp.NewAPIHandler(baseapp.APIHandlerOpts{
|
||||||
Reader: opts.DataConnector,
|
Reader: opts.DataConnector,
|
||||||
@ -71,6 +75,7 @@ func NewAPIHandler(opts APIHandlerOptions, signoz *signoz.SigNoz) (*APIHandler,
|
|||||||
UseTraceNewSchema: opts.UseTraceNewSchema,
|
UseTraceNewSchema: opts.UseTraceNewSchema,
|
||||||
AlertmanagerAPI: alertmanager.NewAPI(signoz.Alertmanager),
|
AlertmanagerAPI: alertmanager.NewAPI(signoz.Alertmanager),
|
||||||
Signoz: signoz,
|
Signoz: signoz,
|
||||||
|
Preference: preference,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -44,7 +44,6 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/query-service/app/logparsingpipeline"
|
"github.com/SigNoz/signoz/pkg/query-service/app/logparsingpipeline"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/opamp"
|
"github.com/SigNoz/signoz/pkg/query-service/app/opamp"
|
||||||
opAmpModel "github.com/SigNoz/signoz/pkg/query-service/app/opamp/model"
|
opAmpModel "github.com/SigNoz/signoz/pkg/query-service/app/opamp/model"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/preferences"
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/cache"
|
"github.com/SigNoz/signoz/pkg/query-service/cache"
|
||||||
baseconst "github.com/SigNoz/signoz/pkg/query-service/constants"
|
baseconst "github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/healthcheck"
|
"github.com/SigNoz/signoz/pkg/query-service/healthcheck"
|
||||||
@ -116,10 +115,6 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := preferences.InitDB(serverOptions.SigNoz.SQLStore.SQLxDB()); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := dashboards.InitDB(serverOptions.SigNoz.SQLStore); err != nil {
|
if err := dashboards.InitDB(serverOptions.SigNoz.SQLStore); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,6 +15,16 @@ var (
|
|||||||
Text = "text"
|
Text = "text"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Org = "org"
|
||||||
|
User = "user"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
OrgReference = `("org_id") REFERENCES "organizations" ("id")`
|
||||||
|
UserReference = `("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE`
|
||||||
|
)
|
||||||
|
|
||||||
type dialect struct {
|
type dialect struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,7 +236,10 @@ func (dialect *dialect) AddNotNullDefaultToColumn(ctx context.Context, bun bun.I
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, cb func(context.Context) error) error {
|
func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, reference string, cb func(context.Context) error) error {
|
||||||
|
if reference == "" {
|
||||||
|
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "cannot run migration without reference")
|
||||||
|
}
|
||||||
oldTableName := bun.Dialect().Tables().Get(reflect.TypeOf(oldModel)).Name
|
oldTableName := bun.Dialect().Tables().Get(reflect.TypeOf(oldModel)).Name
|
||||||
newTableName := bun.Dialect().Tables().Get(reflect.TypeOf(newModel)).Name
|
newTableName := bun.Dialect().Tables().Get(reflect.TypeOf(newModel)).Name
|
||||||
|
|
||||||
@ -237,11 +251,74 @@ func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldMo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fkReference := ""
|
||||||
|
if reference == Org {
|
||||||
|
fkReference = OrgReference
|
||||||
|
} else if reference == User {
|
||||||
|
fkReference = UserReference
|
||||||
|
}
|
||||||
|
|
||||||
_, err = bun.
|
_, err = bun.
|
||||||
NewCreateTable().
|
NewCreateTable().
|
||||||
IfNotExists().
|
IfNotExists().
|
||||||
Model(newModel).
|
Model(newModel).
|
||||||
ForeignKey(`("org_id") REFERENCES "organizations" ("id")`).
|
ForeignKey(fkReference).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cb(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bun.
|
||||||
|
NewDropTable().
|
||||||
|
IfExists().
|
||||||
|
Model(oldModel).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bun.
|
||||||
|
ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s RENAME TO %s", newTableName, oldTableName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *dialect) AddPrimaryKey(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, reference string, cb func(context.Context) error) error {
|
||||||
|
if reference == "" {
|
||||||
|
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "cannot run migration without reference")
|
||||||
|
}
|
||||||
|
oldTableName := bun.Dialect().Tables().Get(reflect.TypeOf(oldModel)).Name
|
||||||
|
newTableName := bun.Dialect().Tables().Get(reflect.TypeOf(newModel)).Name
|
||||||
|
|
||||||
|
identityExists, err := dialect.ColumnExists(ctx, bun, oldTableName, Identity)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if identityExists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fkReference := ""
|
||||||
|
if reference == Org {
|
||||||
|
fkReference = OrgReference
|
||||||
|
} else if reference == User {
|
||||||
|
fkReference = UserReference
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bun.
|
||||||
|
NewCreateTable().
|
||||||
|
IfNotExists().
|
||||||
|
Model(newModel).
|
||||||
|
ForeignKey(fkReference).
|
||||||
Exec(ctx)
|
Exec(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
149
pkg/modules/preference/api.go
Normal file
149
pkg/modules/preference/api.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package preference
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
errorsV2 "github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/http/render"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/preferencetypes"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
type API interface {
|
||||||
|
GetOrgPreference(http.ResponseWriter, *http.Request)
|
||||||
|
UpdateOrgPreference(http.ResponseWriter, *http.Request)
|
||||||
|
GetAllOrgPreferences(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
GetUserPreference(http.ResponseWriter, *http.Request)
|
||||||
|
UpdateUserPreference(http.ResponseWriter, *http.Request)
|
||||||
|
GetAllUserPreferences(http.ResponseWriter, *http.Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
type preferenceAPI struct {
|
||||||
|
usecase Usecase
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAPI(usecase Usecase) API {
|
||||||
|
return &preferenceAPI{usecase: usecase}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *preferenceAPI) GetOrgPreference(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
preferenceId := mux.Vars(r)["preferenceId"]
|
||||||
|
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
render.Error(rw, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
preference, err := p.usecase.GetOrgPreference(
|
||||||
|
r.Context(), preferenceId, claims.OrgID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusOK, preference)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *preferenceAPI) UpdateOrgPreference(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
preferenceId := mux.Vars(r)["preferenceId"]
|
||||||
|
req := preferencetypes.UpdatablePreference{}
|
||||||
|
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
render.Error(rw, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = p.usecase.UpdateOrgPreference(r.Context(), preferenceId, req.PreferenceValue, claims.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusNoContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *preferenceAPI) GetAllOrgPreferences(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
render.Error(rw, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
preferences, err := p.usecase.GetAllOrgPreferences(
|
||||||
|
r.Context(), claims.OrgID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusOK, preferences)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *preferenceAPI) GetUserPreference(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
preferenceId := mux.Vars(r)["preferenceId"]
|
||||||
|
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
render.Error(rw, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
preference, err := p.usecase.GetUserPreference(
|
||||||
|
r.Context(), preferenceId, claims.OrgID, claims.UserID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusOK, preference)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *preferenceAPI) UpdateUserPreference(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
preferenceId := mux.Vars(r)["preferenceId"]
|
||||||
|
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
render.Error(rw, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req := preferencetypes.UpdatablePreference{}
|
||||||
|
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = p.usecase.UpdateUserPreference(r.Context(), preferenceId, req.PreferenceValue, claims.UserID)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusNoContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *preferenceAPI) GetAllUserPreferences(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
render.Error(rw, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
preferences, err := p.usecase.GetAllUserPreferences(
|
||||||
|
r.Context(), claims.OrgID, claims.UserID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(rw, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Success(rw, http.StatusOK, preferences)
|
||||||
|
}
|
278
pkg/modules/preference/core/preference.go
Normal file
278
pkg/modules/preference/core/preference.go
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/preferencetypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type usecase struct {
|
||||||
|
store preferencetypes.PreferenceStore
|
||||||
|
defaultMap map[string]preferencetypes.Preference
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPreference(store preferencetypes.PreferenceStore, defaultMap map[string]preferencetypes.Preference) preference.Usecase {
|
||||||
|
return &usecase{store: store, defaultMap: defaultMap}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usecase *usecase) GetOrgPreference(ctx context.Context, preferenceID string, orgID string) (*preferencetypes.GettablePreference, error) {
|
||||||
|
preference, seen := usecase.defaultMap[preferenceID]
|
||||||
|
if !seen {
|
||||||
|
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, fmt.Sprintf("no such preferenceID exists: %s", preferenceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
isPreferenceEnabled := preference.IsEnabledForScope(preferencetypes.OrgAllowedScope)
|
||||||
|
if !isPreferenceEnabled {
|
||||||
|
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, fmt.Sprintf("preference is not enabled at org scope: %s", preferenceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
orgPreference, err := usecase.store.GetOrgPreference(ctx, orgID, preferenceID)
|
||||||
|
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, fmt.Sprintf("error in fetching the org preference: %s", preferenceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &preferencetypes.GettablePreference{
|
||||||
|
PreferenceID: preferenceID,
|
||||||
|
PreferenceValue: preference.SanitizeValue(orgPreference.PreferenceValue),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usecase *usecase) UpdateOrgPreference(ctx context.Context, preferenceID string, preferenceValue interface{}, orgID string) error {
|
||||||
|
preference, seen := usecase.defaultMap[preferenceID]
|
||||||
|
if !seen {
|
||||||
|
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, fmt.Sprintf("no such preferenceID exists: %s", preferenceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
isPreferenceEnabled := preference.IsEnabledForScope(preferencetypes.OrgAllowedScope)
|
||||||
|
if !isPreferenceEnabled {
|
||||||
|
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, fmt.Sprintf("preference is not enabled at org scope: %s", preferenceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
err := preference.IsValidValue(preferenceValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
storablePreferenceValue, encodeErr := json.Marshal(preferenceValue)
|
||||||
|
if encodeErr != nil {
|
||||||
|
return errors.Wrapf(encodeErr, errors.TypeInvalidInput, errors.CodeInvalidInput, "error in encoding the preference value")
|
||||||
|
}
|
||||||
|
|
||||||
|
orgPreference, dberr := usecase.store.GetOrgPreference(ctx, orgID, preferenceID)
|
||||||
|
if dberr != nil && dberr != sql.ErrNoRows {
|
||||||
|
return errors.Wrapf(dberr, errors.TypeInternal, errors.CodeInternal, "error in getting the preference value")
|
||||||
|
}
|
||||||
|
|
||||||
|
if dberr != nil {
|
||||||
|
orgPreference.ID = valuer.GenerateUUID()
|
||||||
|
orgPreference.PreferenceID = preferenceID
|
||||||
|
orgPreference.PreferenceValue = string(storablePreferenceValue)
|
||||||
|
orgPreference.OrgID = orgID
|
||||||
|
} else {
|
||||||
|
orgPreference.PreferenceValue = string(storablePreferenceValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
dberr = usecase.store.UpsertOrgPreference(ctx, orgPreference)
|
||||||
|
if dberr != nil {
|
||||||
|
return errors.Wrapf(dberr, errors.TypeInternal, errors.CodeInternal, "error in setting the preference value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usecase *usecase) GetAllOrgPreferences(ctx context.Context, orgID string) ([]*preferencetypes.PreferenceWithValue, error) {
|
||||||
|
allOrgPreferences := []*preferencetypes.PreferenceWithValue{}
|
||||||
|
orgPreferences, err := usecase.store.GetAllOrgPreferences(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "error in setting all org preference values")
|
||||||
|
}
|
||||||
|
|
||||||
|
preferenceValueMap := map[string]interface{}{}
|
||||||
|
for _, preferenceValue := range orgPreferences {
|
||||||
|
preferenceValueMap[preferenceValue.PreferenceID] = preferenceValue.PreferenceValue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, preference := range usecase.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
|
||||||
|
}
|
||||||
|
|
||||||
|
preferenceWithValue.Value = preference.SanitizeValue(preferenceWithValue.Value)
|
||||||
|
allOrgPreferences = append(allOrgPreferences, preferenceWithValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allOrgPreferences, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usecase *usecase) GetUserPreference(ctx context.Context, preferenceID string, orgID string, userID string) (*preferencetypes.GettablePreference, error) {
|
||||||
|
preference, seen := usecase.defaultMap[preferenceID]
|
||||||
|
if !seen {
|
||||||
|
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, fmt.Sprintf("no such preferenceID exists: %s", preferenceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
preferenceValue := preferencetypes.GettablePreference{
|
||||||
|
PreferenceID: preferenceID,
|
||||||
|
PreferenceValue: preference.DefaultValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
isPreferenceEnabledAtUserScope := preference.IsEnabledForScope(preferencetypes.UserAllowedScope)
|
||||||
|
if !isPreferenceEnabledAtUserScope {
|
||||||
|
return nil, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, fmt.Sprintf("preference is not enabled at user scope: %s", preferenceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
isPreferenceEnabledAtOrgScope := preference.IsEnabledForScope(preferencetypes.OrgAllowedScope)
|
||||||
|
if isPreferenceEnabledAtOrgScope {
|
||||||
|
orgPreference, err := usecase.store.GetOrgPreference(ctx, orgID, preferenceID)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, fmt.Sprintf("error in fetching the org preference: %s", preferenceID))
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
preferenceValue.PreferenceValue = orgPreference.PreferenceValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userPreference, err := usecase.store.GetUserPreference(ctx, userID, preferenceID)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, fmt.Sprintf("error in fetching the user preference: %s", preferenceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
preferenceValue.PreferenceValue = userPreference.PreferenceValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return &preferencetypes.GettablePreference{
|
||||||
|
PreferenceID: preferenceValue.PreferenceID,
|
||||||
|
PreferenceValue: preference.SanitizeValue(preferenceValue.PreferenceValue),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usecase *usecase) UpdateUserPreference(ctx context.Context, preferenceID string, preferenceValue interface{}, userID string) error {
|
||||||
|
preference, seen := usecase.defaultMap[preferenceID]
|
||||||
|
if !seen {
|
||||||
|
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, fmt.Sprintf("no such preferenceID exists: %s", preferenceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
isPreferenceEnabledAtUserScope := preference.IsEnabledForScope(preferencetypes.UserAllowedScope)
|
||||||
|
if !isPreferenceEnabledAtUserScope {
|
||||||
|
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, fmt.Sprintf("preference is not enabled at user scope: %s", preferenceID))
|
||||||
|
}
|
||||||
|
|
||||||
|
err := preference.IsValidValue(preferenceValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
storablePreferenceValue, encodeErr := json.Marshal(preferenceValue)
|
||||||
|
if encodeErr != nil {
|
||||||
|
return errors.Wrapf(encodeErr, errors.TypeInvalidInput, errors.CodeInvalidInput, "error in encoding the preference value")
|
||||||
|
}
|
||||||
|
|
||||||
|
userPreference, dberr := usecase.store.GetUserPreference(ctx, userID, preferenceID)
|
||||||
|
if dberr != nil && dberr != sql.ErrNoRows {
|
||||||
|
return errors.Wrapf(dberr, errors.TypeInternal, errors.CodeInternal, "error in getting the preference value")
|
||||||
|
}
|
||||||
|
|
||||||
|
if dberr != nil {
|
||||||
|
userPreference.ID = valuer.GenerateUUID()
|
||||||
|
userPreference.PreferenceID = preferenceID
|
||||||
|
userPreference.PreferenceValue = string(storablePreferenceValue)
|
||||||
|
userPreference.UserID = userID
|
||||||
|
} else {
|
||||||
|
userPreference.PreferenceValue = string(storablePreferenceValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
dberr = usecase.store.UpsertUserPreference(ctx, userPreference)
|
||||||
|
if dberr != nil {
|
||||||
|
return errors.Wrapf(dberr, errors.TypeInternal, errors.CodeInternal, "error in setting the preference value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (usecase *usecase) GetAllUserPreferences(ctx context.Context, orgID string, userID string) ([]*preferencetypes.PreferenceWithValue, error) {
|
||||||
|
allUserPreferences := []*preferencetypes.PreferenceWithValue{}
|
||||||
|
|
||||||
|
orgPreferences, err := usecase.store.GetAllOrgPreferences(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 orgPreferences {
|
||||||
|
preferenceOrgValueMap[preferenceValue.PreferenceID] = preferenceValue.PreferenceValue
|
||||||
|
}
|
||||||
|
|
||||||
|
userPreferences, err := usecase.store.GetAllUserPreferences(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 userPreferences {
|
||||||
|
preferenceUserValueMap[preferenceValue.PreferenceID] = preferenceValue.PreferenceValue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, preference := range usecase.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)
|
||||||
|
allUserPreferences = append(allUserPreferences, preferenceWithValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allUserPreferences, nil
|
||||||
|
}
|
116
pkg/modules/preference/core/store.go
Normal file
116
pkg/modules/preference/core/store.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/preferencetypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type store struct {
|
||||||
|
store sqlstore.SQLStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStore(db sqlstore.SQLStore) preferencetypes.PreferenceStore {
|
||||||
|
return &store{store: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) GetOrgPreference(ctx context.Context, orgID string, preferenceID string) (*preferencetypes.StorableOrgPreference, error) {
|
||||||
|
orgPreference := new(preferencetypes.StorableOrgPreference)
|
||||||
|
err := store.
|
||||||
|
store.
|
||||||
|
BunDB().
|
||||||
|
NewSelect().
|
||||||
|
Model(orgPreference).
|
||||||
|
Where("preference_id = ?", preferenceID).
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
Scan(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return orgPreference, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return orgPreference, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) GetAllOrgPreferences(ctx context.Context, orgID string) ([]*preferencetypes.StorableOrgPreference, error) {
|
||||||
|
orgPreferences := make([]*preferencetypes.StorableOrgPreference, 0)
|
||||||
|
err := store.
|
||||||
|
store.
|
||||||
|
BunDB().
|
||||||
|
NewSelect().
|
||||||
|
Model(&orgPreferences).
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
Scan(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return orgPreferences, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return orgPreferences, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) UpsertOrgPreference(ctx context.Context, orgPreference *preferencetypes.StorableOrgPreference) error {
|
||||||
|
_, err := store.
|
||||||
|
store.
|
||||||
|
BunDB().
|
||||||
|
NewInsert().
|
||||||
|
Model(orgPreference).
|
||||||
|
On("CONFLICT (id) DO UPDATE").
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) GetUserPreference(ctx context.Context, userID string, preferenceID string) (*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
|
||||||
|
}
|
||||||
|
|
||||||
|
return userPreference, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) GetAllUserPreferences(ctx context.Context, userID string) ([]*preferencetypes.StorableUserPreference, error) {
|
||||||
|
userPreferences := make([]*preferencetypes.StorableUserPreference, 0)
|
||||||
|
err := store.
|
||||||
|
store.
|
||||||
|
BunDB().
|
||||||
|
NewSelect().
|
||||||
|
Model(&userPreferences).
|
||||||
|
Where("user_id = ?", userID).
|
||||||
|
Scan(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return userPreferences, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return userPreferences, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) UpsertUserPreference(ctx context.Context, userPreference *preferencetypes.StorableUserPreference) error {
|
||||||
|
_, err := store.
|
||||||
|
store.
|
||||||
|
BunDB().
|
||||||
|
NewInsert().
|
||||||
|
Model(userPreference).
|
||||||
|
On("CONFLICT (id) DO UPDATE").
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
17
pkg/modules/preference/usecase.go
Normal file
17
pkg/modules/preference/usecase.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package preference
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/preferencetypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Usecase interface {
|
||||||
|
GetOrgPreference(ctx context.Context, preferenceId string, orgId string) (*preferencetypes.GettablePreference, error)
|
||||||
|
UpdateOrgPreference(ctx context.Context, preferenceId string, preferenceValue interface{}, orgId string) error
|
||||||
|
GetAllOrgPreferences(ctx context.Context, orgId string) ([]*preferencetypes.PreferenceWithValue, error)
|
||||||
|
|
||||||
|
GetUserPreference(ctx context.Context, preferenceId string, orgId string, userId string) (*preferencetypes.GettablePreference, error)
|
||||||
|
UpdateUserPreference(ctx context.Context, preferenceId string, preferenceValue interface{}, userId string) error
|
||||||
|
GetAllUserPreferences(ctx context.Context, orgId string, userId string) ([]*preferencetypes.PreferenceWithValue, error)
|
||||||
|
}
|
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||||
errorsV2 "github.com/SigNoz/signoz/pkg/errors"
|
errorsV2 "github.com/SigNoz/signoz/pkg/errors"
|
||||||
"github.com/SigNoz/signoz/pkg/http/render"
|
"github.com/SigNoz/signoz/pkg/http/render"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/metricsexplorer"
|
"github.com/SigNoz/signoz/pkg/query-service/app/metricsexplorer"
|
||||||
"github.com/SigNoz/signoz/pkg/signoz"
|
"github.com/SigNoz/signoz/pkg/signoz"
|
||||||
"github.com/SigNoz/signoz/pkg/valuer"
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
@ -44,7 +45,6 @@ import (
|
|||||||
logsv4 "github.com/SigNoz/signoz/pkg/query-service/app/logs/v4"
|
logsv4 "github.com/SigNoz/signoz/pkg/query-service/app/logs/v4"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/metrics"
|
"github.com/SigNoz/signoz/pkg/query-service/app/metrics"
|
||||||
metricsv3 "github.com/SigNoz/signoz/pkg/query-service/app/metrics/v3"
|
metricsv3 "github.com/SigNoz/signoz/pkg/query-service/app/metrics/v3"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/preferences"
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/querier"
|
"github.com/SigNoz/signoz/pkg/query-service/app/querier"
|
||||||
querierV2 "github.com/SigNoz/signoz/pkg/query-service/app/querier/v2"
|
querierV2 "github.com/SigNoz/signoz/pkg/query-service/app/querier/v2"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder"
|
"github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder"
|
||||||
@ -142,6 +142,8 @@ type APIHandler struct {
|
|||||||
AlertmanagerAPI *alertmanager.API
|
AlertmanagerAPI *alertmanager.API
|
||||||
|
|
||||||
Signoz *signoz.SigNoz
|
Signoz *signoz.SigNoz
|
||||||
|
|
||||||
|
Preference preference.API
|
||||||
}
|
}
|
||||||
|
|
||||||
type APIHandlerOpts struct {
|
type APIHandlerOpts struct {
|
||||||
@ -187,6 +189,8 @@ type APIHandlerOpts struct {
|
|||||||
AlertmanagerAPI *alertmanager.API
|
AlertmanagerAPI *alertmanager.API
|
||||||
|
|
||||||
Signoz *signoz.SigNoz
|
Signoz *signoz.SigNoz
|
||||||
|
|
||||||
|
Preference preference.API
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAPIHandler returns an APIHandler
|
// NewAPIHandler returns an APIHandler
|
||||||
@ -257,6 +261,7 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) {
|
|||||||
SummaryService: summaryService,
|
SummaryService: summaryService,
|
||||||
AlertmanagerAPI: opts.AlertmanagerAPI,
|
AlertmanagerAPI: opts.AlertmanagerAPI,
|
||||||
Signoz: opts.Signoz,
|
Signoz: opts.Signoz,
|
||||||
|
Preference: opts.Preference,
|
||||||
}
|
}
|
||||||
|
|
||||||
logsQueryBuilder := logsv3.PrepareLogsQuery
|
logsQueryBuilder := logsv3.PrepareLogsQuery
|
||||||
@ -3415,132 +3420,37 @@ func (aH *APIHandler) getProducerConsumerEval(
|
|||||||
func (aH *APIHandler) getUserPreference(
|
func (aH *APIHandler) getUserPreference(
|
||||||
w http.ResponseWriter, r *http.Request,
|
w http.ResponseWriter, r *http.Request,
|
||||||
) {
|
) {
|
||||||
preferenceId := mux.Vars(r)["preferenceId"]
|
aH.Preference.GetUserPreference(w, r)
|
||||||
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
|
||||||
if !ok {
|
|
||||||
render.Error(w, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
preference, apiErr := preferences.GetUserPreference(
|
|
||||||
r.Context(), preferenceId, claims.OrgID, claims.UserID,
|
|
||||||
)
|
|
||||||
if apiErr != nil {
|
|
||||||
RespondError(w, apiErr, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
aH.Respond(w, preference)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) updateUserPreference(
|
func (aH *APIHandler) updateUserPreference(
|
||||||
w http.ResponseWriter, r *http.Request,
|
w http.ResponseWriter, r *http.Request,
|
||||||
) {
|
) {
|
||||||
preferenceId := mux.Vars(r)["preferenceId"]
|
aH.Preference.UpdateUserPreference(w, r)
|
||||||
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
|
||||||
if !ok {
|
|
||||||
render.Error(w, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req := preferences.UpdatePreference{}
|
|
||||||
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
RespondError(w, model.BadRequest(err), nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
preference, apiErr := preferences.UpdateUserPreference(r.Context(), preferenceId, req.PreferenceValue, claims.UserID)
|
|
||||||
if apiErr != nil {
|
|
||||||
RespondError(w, apiErr, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
aH.Respond(w, preference)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) getAllUserPreferences(
|
func (aH *APIHandler) getAllUserPreferences(
|
||||||
w http.ResponseWriter, r *http.Request,
|
w http.ResponseWriter, r *http.Request,
|
||||||
) {
|
) {
|
||||||
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
aH.Preference.GetAllUserPreferences(w, r)
|
||||||
if !ok {
|
|
||||||
render.Error(w, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
preference, apiErr := preferences.GetAllUserPreferences(
|
|
||||||
r.Context(), claims.OrgID, claims.UserID,
|
|
||||||
)
|
|
||||||
if apiErr != nil {
|
|
||||||
RespondError(w, apiErr, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
aH.Respond(w, preference)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) getOrgPreference(
|
func (aH *APIHandler) getOrgPreference(
|
||||||
w http.ResponseWriter, r *http.Request,
|
w http.ResponseWriter, r *http.Request,
|
||||||
) {
|
) {
|
||||||
preferenceId := mux.Vars(r)["preferenceId"]
|
aH.Preference.GetOrgPreference(w, r)
|
||||||
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
|
||||||
if !ok {
|
|
||||||
render.Error(w, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
preference, apiErr := preferences.GetOrgPreference(
|
|
||||||
r.Context(), preferenceId, claims.OrgID,
|
|
||||||
)
|
|
||||||
if apiErr != nil {
|
|
||||||
RespondError(w, apiErr, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
aH.Respond(w, preference)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) updateOrgPreference(
|
func (aH *APIHandler) updateOrgPreference(
|
||||||
w http.ResponseWriter, r *http.Request,
|
w http.ResponseWriter, r *http.Request,
|
||||||
) {
|
) {
|
||||||
preferenceId := mux.Vars(r)["preferenceId"]
|
aH.Preference.UpdateOrgPreference(w, r)
|
||||||
req := preferences.UpdatePreference{}
|
|
||||||
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
|
||||||
if !ok {
|
|
||||||
render.Error(w, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
RespondError(w, model.BadRequest(err), nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
preference, apiErr := preferences.UpdateOrgPreference(r.Context(), preferenceId, req.PreferenceValue, claims.OrgID)
|
|
||||||
if apiErr != nil {
|
|
||||||
RespondError(w, apiErr, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
aH.Respond(w, preference)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) getAllOrgPreferences(
|
func (aH *APIHandler) getAllOrgPreferences(
|
||||||
w http.ResponseWriter, r *http.Request,
|
w http.ResponseWriter, r *http.Request,
|
||||||
) {
|
) {
|
||||||
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
aH.Preference.GetAllOrgPreferences(w, r)
|
||||||
if !ok {
|
|
||||||
render.Error(w, errorsV2.Newf(errorsV2.TypeUnauthenticated, errorsV2.CodeUnauthenticated, "unauthenticated"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
preference, apiErr := preferences.GetAllOrgPreferences(
|
|
||||||
r.Context(), claims.OrgID,
|
|
||||||
)
|
|
||||||
if apiErr != nil {
|
|
||||||
RespondError(w, apiErr, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
aH.Respond(w, preference)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterIntegrationRoutes Registers all Integrations
|
// RegisterIntegrationRoutes Registers all Integrations
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
package preferences
|
|
||||||
|
|
||||||
var preferenceMap = map[string]Preference{
|
|
||||||
"ORG_ONBOARDING": {
|
|
||||||
Key: "ORG_ONBOARDING",
|
|
||||||
Name: "Organisation Onboarding",
|
|
||||||
Description: "Organisation Onboarding",
|
|
||||||
ValueType: "boolean",
|
|
||||||
DefaultValue: false,
|
|
||||||
AllowedValues: []interface{}{true, false},
|
|
||||||
IsDiscreteValues: true,
|
|
||||||
AllowedScopes: []string{"org"},
|
|
||||||
},
|
|
||||||
"WELCOME_CHECKLIST_DO_LATER": {
|
|
||||||
Key: "WELCOME_CHECKLIST_DO_LATER",
|
|
||||||
Name: "Welcome Checklist Do Later",
|
|
||||||
Description: "Welcome Checklist Do Later",
|
|
||||||
ValueType: "boolean",
|
|
||||||
DefaultValue: false,
|
|
||||||
AllowedValues: []interface{}{true, false},
|
|
||||||
IsDiscreteValues: true,
|
|
||||||
AllowedScopes: []string{"user"},
|
|
||||||
},
|
|
||||||
"WELCOME_CHECKLIST_SEND_LOGS_SKIPPED": {
|
|
||||||
Key: "WELCOME_CHECKLIST_SEND_LOGS_SKIPPED",
|
|
||||||
Name: "Welcome Checklist Send Logs Skipped",
|
|
||||||
Description: "Welcome Checklist Send Logs Skipped",
|
|
||||||
ValueType: "boolean",
|
|
||||||
DefaultValue: false,
|
|
||||||
AllowedValues: []interface{}{true, false},
|
|
||||||
IsDiscreteValues: true,
|
|
||||||
AllowedScopes: []string{"user"},
|
|
||||||
},
|
|
||||||
"WELCOME_CHECKLIST_SEND_TRACES_SKIPPED": {
|
|
||||||
Key: "WELCOME_CHECKLIST_SEND_TRACES_SKIPPED",
|
|
||||||
Name: "Welcome Checklist Send Traces Skipped",
|
|
||||||
Description: "Welcome Checklist Send Traces Skipped",
|
|
||||||
ValueType: "boolean",
|
|
||||||
DefaultValue: false,
|
|
||||||
AllowedValues: []interface{}{true, false},
|
|
||||||
IsDiscreteValues: true,
|
|
||||||
AllowedScopes: []string{"user"},
|
|
||||||
},
|
|
||||||
"WELCOME_CHECKLIST_SEND_INFRA_METRICS_SKIPPED": {
|
|
||||||
Key: "WELCOME_CHECKLIST_SEND_INFRA_METRICS_SKIPPED",
|
|
||||||
Name: "Welcome Checklist Send Infra Metrics Skipped",
|
|
||||||
Description: "Welcome Checklist Send Infra Metrics Skipped",
|
|
||||||
ValueType: "boolean",
|
|
||||||
DefaultValue: false,
|
|
||||||
AllowedValues: []interface{}{true, false},
|
|
||||||
IsDiscreteValues: true,
|
|
||||||
AllowedScopes: []string{"user"},
|
|
||||||
},
|
|
||||||
"WELCOME_CHECKLIST_SETUP_DASHBOARDS_SKIPPED": {
|
|
||||||
Key: "WELCOME_CHECKLIST_SETUP_DASHBOARDS_SKIPPED",
|
|
||||||
Name: "Welcome Checklist Setup Dashboards Skipped",
|
|
||||||
Description: "Welcome Checklist Setup Dashboards Skipped",
|
|
||||||
ValueType: "boolean",
|
|
||||||
DefaultValue: false,
|
|
||||||
AllowedValues: []interface{}{true, false},
|
|
||||||
IsDiscreteValues: true,
|
|
||||||
AllowedScopes: []string{"user"},
|
|
||||||
},
|
|
||||||
"WELCOME_CHECKLIST_SETUP_ALERTS_SKIPPED": {
|
|
||||||
Key: "WELCOME_CHECKLIST_SETUP_ALERTS_SKIPPED",
|
|
||||||
Name: "Welcome Checklist Setup Alerts Skipped",
|
|
||||||
Description: "Welcome Checklist Setup Alerts Skipped",
|
|
||||||
ValueType: "boolean",
|
|
||||||
DefaultValue: false,
|
|
||||||
AllowedValues: []interface{}{true, false},
|
|
||||||
IsDiscreteValues: true,
|
|
||||||
AllowedScopes: []string{"user"},
|
|
||||||
},
|
|
||||||
"WELCOME_CHECKLIST_SETUP_SAVED_VIEW_SKIPPED": {
|
|
||||||
Key: "WELCOME_CHECKLIST_SETUP_SAVED_VIEW_SKIPPED",
|
|
||||||
Name: "Welcome Checklist Setup Saved View Skipped",
|
|
||||||
Description: "Welcome Checklist Setup Saved View Skipped",
|
|
||||||
ValueType: "boolean",
|
|
||||||
DefaultValue: false,
|
|
||||||
AllowedValues: []interface{}{true, false},
|
|
||||||
IsDiscreteValues: true,
|
|
||||||
AllowedScopes: []string{"user"},
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,500 +0,0 @@
|
|||||||
package preferences
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Range struct {
|
|
||||||
Min int64 `json:"min"`
|
|
||||||
Max int64 `json:"max"`
|
|
||||||
}
|
|
||||||
|
|
||||||
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"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Preference) ErrorValueTypeMismatch() *model.ApiError {
|
|
||||||
return &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("the preference value is not of expected type: %s", p.ValueType)}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
PreferenceValueTypeInteger string = "integer"
|
|
||||||
PreferenceValueTypeFloat string = "float"
|
|
||||||
PreferenceValueTypeString string = "string"
|
|
||||||
PreferenceValueTypeBoolean string = "boolean"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
OrgAllowedScope string = "org"
|
|
||||||
UserAllowedScope string = "user"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Preference) checkIfInAllowedValues(preferenceValue interface{}) (bool, *model.ApiError) {
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Preference) IsValidValue(preferenceValue interface{}) *model.ApiError {
|
|
||||||
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 &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isPreferenceEnabledForGivenScope
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Preference) SanitizeValue(preferenceValue interface{}) interface{} {
|
|
||||||
switch p.ValueType {
|
|
||||||
case PreferenceValueTypeBoolean:
|
|
||||||
if preferenceValue == "1" || preferenceValue == true {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return preferenceValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type AllPreferences struct {
|
|
||||||
Preference
|
|
||||||
Value interface{} `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PreferenceKV struct {
|
|
||||||
PreferenceId string `json:"preference_id" db:"preference_id"`
|
|
||||||
PreferenceValue interface{} `json:"preference_value" db:"preference_value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdatePreference struct {
|
|
||||||
PreferenceValue interface{} `json:"preference_value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var db *sqlx.DB
|
|
||||||
|
|
||||||
func InitDB(inputDB *sqlx.DB) error {
|
|
||||||
db = inputDB
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// org preference functions
|
|
||||||
func GetOrgPreference(ctx context.Context, preferenceId string, orgId string) (*PreferenceKV, *model.ApiError) {
|
|
||||||
// check if the preference key exists or not
|
|
||||||
preference, seen := preferenceMap[preferenceId]
|
|
||||||
if !seen {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no such preferenceId exists: %s", preferenceId)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the preference is enabled for org scope or not
|
|
||||||
isPreferenceEnabled := preference.IsEnabledForScope(OrgAllowedScope)
|
|
||||||
if !isPreferenceEnabled {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("preference is not enabled at org scope: %s", preferenceId)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch the value from the database
|
|
||||||
var orgPreference PreferenceKV
|
|
||||||
query := `SELECT preference_id , preference_value FROM org_preference WHERE preference_id=$1 AND org_id=$2;`
|
|
||||||
err := db.Get(&orgPreference, query, preferenceId, orgId)
|
|
||||||
|
|
||||||
// if the value doesn't exist in db then return the default value
|
|
||||||
if err != nil {
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return &PreferenceKV{
|
|
||||||
PreferenceId: preferenceId,
|
|
||||||
PreferenceValue: preference.DefaultValue,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in fetching the org preference: %s", err.Error())}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// else return the value fetched from the org_preference table
|
|
||||||
return &PreferenceKV{
|
|
||||||
PreferenceId: preferenceId,
|
|
||||||
PreferenceValue: preference.SanitizeValue(orgPreference.PreferenceValue),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateOrgPreference(ctx context.Context, preferenceId string, preferenceValue interface{}, orgId string) (*PreferenceKV, *model.ApiError) {
|
|
||||||
// check if the preference key exists or not
|
|
||||||
preference, seen := preferenceMap[preferenceId]
|
|
||||||
if !seen {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no such preferenceId exists: %s", preferenceId)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the preference is enabled at org scope or not
|
|
||||||
isPreferenceEnabled := preference.IsEnabledForScope(OrgAllowedScope)
|
|
||||||
if !isPreferenceEnabled {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("preference is not enabled at org scope: %s", preferenceId)}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := preference.IsValidValue(preferenceValue)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the values in the org_preference table and return the key and the value
|
|
||||||
query := `INSERT INTO org_preference(preference_id,preference_value,org_id) VALUES($1,$2,$3)
|
|
||||||
ON CONFLICT(preference_id,org_id) DO
|
|
||||||
UPDATE SET preference_value= $2 WHERE preference_id=$1 AND org_id=$3;`
|
|
||||||
|
|
||||||
_, dberr := db.Exec(query, preferenceId, preferenceValue, orgId)
|
|
||||||
|
|
||||||
if dberr != nil {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in setting the preference value: %s", dberr.Error())}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &PreferenceKV{
|
|
||||||
PreferenceId: preferenceId,
|
|
||||||
PreferenceValue: preferenceValue,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAllOrgPreferences(ctx context.Context, orgId string) (*[]AllPreferences, *model.ApiError) {
|
|
||||||
// filter out all the org enabled preferences from the preference variable
|
|
||||||
allOrgPreferences := []AllPreferences{}
|
|
||||||
|
|
||||||
// fetch all the org preference values stored in org_preference table
|
|
||||||
orgPreferenceValues := []PreferenceKV{}
|
|
||||||
|
|
||||||
query := `SELECT preference_id,preference_value FROM org_preference WHERE org_id=$1;`
|
|
||||||
err := db.Select(&orgPreferenceValues, query, orgId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in getting all org preference values: %s", err)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a map of key vs values from the above response
|
|
||||||
preferenceValueMap := map[string]interface{}{}
|
|
||||||
|
|
||||||
for _, preferenceValue := range orgPreferenceValues {
|
|
||||||
preferenceValueMap[preferenceValue.PreferenceId] = preferenceValue.PreferenceValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// update in the above filtered list wherver value present in the map
|
|
||||||
for _, preference := range preferenceMap {
|
|
||||||
isEnabledForOrgScope := preference.IsEnabledForScope(OrgAllowedScope)
|
|
||||||
if isEnabledForOrgScope {
|
|
||||||
preferenceWithValue := AllPreferences{}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
preferenceWithValue.Value = preference.SanitizeValue(preferenceWithValue.Value)
|
|
||||||
allOrgPreferences = append(allOrgPreferences, preferenceWithValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &allOrgPreferences, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// user preference functions
|
|
||||||
func GetUserPreference(ctx context.Context, preferenceId string, orgId string, userId string) (*PreferenceKV, *model.ApiError) {
|
|
||||||
// check if the preference key exists
|
|
||||||
preference, seen := preferenceMap[preferenceId]
|
|
||||||
if !seen {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no such preferenceId exists: %s", preferenceId)}
|
|
||||||
}
|
|
||||||
|
|
||||||
preferenceValue := PreferenceKV{
|
|
||||||
PreferenceId: preferenceId,
|
|
||||||
PreferenceValue: preference.DefaultValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the preference is enabled at user scope
|
|
||||||
isPreferenceEnabledAtUserScope := preference.IsEnabledForScope(UserAllowedScope)
|
|
||||||
if !isPreferenceEnabledAtUserScope {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("preference is not enabled at user scope: %s", preferenceId)}
|
|
||||||
}
|
|
||||||
|
|
||||||
isPreferenceEnabledAtOrgScope := preference.IsEnabledForScope(OrgAllowedScope)
|
|
||||||
// get the value from the org scope if enabled at org scope
|
|
||||||
if isPreferenceEnabledAtOrgScope {
|
|
||||||
orgPreference := PreferenceKV{}
|
|
||||||
|
|
||||||
query := `SELECT preference_id , preference_value FROM org_preference WHERE preference_id=$1 AND org_id=$2;`
|
|
||||||
|
|
||||||
err := db.Get(&orgPreference, query, preferenceId, orgId)
|
|
||||||
|
|
||||||
// if there is error in getting values and its not an empty rows error return from here
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in getting org preference values: %s", err.Error())}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is no error update the preference value with value from org preference
|
|
||||||
if err == nil {
|
|
||||||
preferenceValue.PreferenceValue = orgPreference.PreferenceValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the value from the user_preference table, if exists return this value else the one calculated in the above step
|
|
||||||
userPreference := PreferenceKV{}
|
|
||||||
|
|
||||||
query := `SELECT preference_id, preference_value FROM user_preference WHERE preference_id=$1 AND user_id=$2;`
|
|
||||||
err := db.Get(&userPreference, query, preferenceId, userId)
|
|
||||||
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in getting user preference values: %s", err.Error())}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
preferenceValue.PreferenceValue = userPreference.PreferenceValue
|
|
||||||
}
|
|
||||||
|
|
||||||
return &PreferenceKV{
|
|
||||||
PreferenceId: preferenceValue.PreferenceId,
|
|
||||||
PreferenceValue: preference.SanitizeValue(preferenceValue.PreferenceValue),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateUserPreference(ctx context.Context, preferenceId string, preferenceValue interface{}, userId string) (*PreferenceKV, *model.ApiError) {
|
|
||||||
// check if the preference id is valid
|
|
||||||
preference, seen := preferenceMap[preferenceId]
|
|
||||||
if !seen {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no such preferenceId exists: %s", preferenceId)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the preference is enabled at user scope
|
|
||||||
isPreferenceEnabledAtUserScope := preference.IsEnabledForScope(UserAllowedScope)
|
|
||||||
if !isPreferenceEnabledAtUserScope {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("preference is not enabled at user scope: %s", preferenceId)}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := preference.IsValidValue(preferenceValue)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// update the user preference values
|
|
||||||
query := `INSERT INTO user_preference(preference_id,preference_value,user_id) VALUES($1,$2,$3)
|
|
||||||
ON CONFLICT(preference_id,user_id) DO
|
|
||||||
UPDATE SET preference_value= $2 WHERE preference_id=$1 AND user_id=$3;`
|
|
||||||
|
|
||||||
_, dberrr := db.Exec(query, preferenceId, preferenceValue, userId)
|
|
||||||
|
|
||||||
if dberrr != nil {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in setting the preference value: %s", dberrr.Error())}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &PreferenceKV{
|
|
||||||
PreferenceId: preferenceId,
|
|
||||||
PreferenceValue: preferenceValue,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAllUserPreferences(ctx context.Context, orgId string, userId string) (*[]AllPreferences, *model.ApiError) {
|
|
||||||
allUserPreferences := []AllPreferences{}
|
|
||||||
|
|
||||||
// fetch all the org preference values stored in org_preference table
|
|
||||||
orgPreferenceValues := []PreferenceKV{}
|
|
||||||
|
|
||||||
query := `SELECT preference_id,preference_value FROM org_preference WHERE org_id=$1;`
|
|
||||||
err := db.Select(&orgPreferenceValues, query, orgId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in getting all org preference values: %s", err)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a map of key vs values from the above response
|
|
||||||
preferenceOrgValueMap := map[string]interface{}{}
|
|
||||||
|
|
||||||
for _, preferenceValue := range orgPreferenceValues {
|
|
||||||
preferenceOrgValueMap[preferenceValue.PreferenceId] = preferenceValue.PreferenceValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch all the user preference values stored in user_preference table
|
|
||||||
userPreferenceValues := []PreferenceKV{}
|
|
||||||
|
|
||||||
query = `SELECT preference_id,preference_value FROM user_preference WHERE user_id=$1;`
|
|
||||||
err = db.Select(&userPreferenceValues, query, userId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in getting all user preference values: %s", err)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a map of key vs values from the above response
|
|
||||||
preferenceUserValueMap := map[string]interface{}{}
|
|
||||||
|
|
||||||
for _, preferenceValue := range userPreferenceValues {
|
|
||||||
preferenceUserValueMap[preferenceValue.PreferenceId] = preferenceValue.PreferenceValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// update in the above filtered list wherver value present in the map
|
|
||||||
for _, preference := range preferenceMap {
|
|
||||||
isEnabledForUserScope := preference.IsEnabledForScope(UserAllowedScope)
|
|
||||||
|
|
||||||
if isEnabledForUserScope {
|
|
||||||
preferenceWithValue := AllPreferences{}
|
|
||||||
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(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)
|
|
||||||
allUserPreferences = append(allUserPreferences, preferenceWithValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &allUserPreferences, nil
|
|
||||||
}
|
|
@ -14,6 +14,8 @@ import (
|
|||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||||
|
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||||
|
preferencecore "github.com/SigNoz/signoz/pkg/modules/preference/core"
|
||||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/agentConf"
|
"github.com/SigNoz/signoz/pkg/query-service/agentConf"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/clickhouseReader"
|
"github.com/SigNoz/signoz/pkg/query-service/app/clickhouseReader"
|
||||||
@ -23,12 +25,12 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/query-service/app/logparsingpipeline"
|
"github.com/SigNoz/signoz/pkg/query-service/app/logparsingpipeline"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/opamp"
|
"github.com/SigNoz/signoz/pkg/query-service/app/opamp"
|
||||||
opAmpModel "github.com/SigNoz/signoz/pkg/query-service/app/opamp/model"
|
opAmpModel "github.com/SigNoz/signoz/pkg/query-service/app/opamp/model"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/app/preferences"
|
|
||||||
"github.com/SigNoz/signoz/pkg/signoz"
|
"github.com/SigNoz/signoz/pkg/signoz"
|
||||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||||
"github.com/SigNoz/signoz/pkg/types"
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/preferencetypes"
|
||||||
"github.com/SigNoz/signoz/pkg/web"
|
"github.com/SigNoz/signoz/pkg/web"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
"github.com/soheilhy/cmux"
|
"github.com/soheilhy/cmux"
|
||||||
@ -98,10 +100,6 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := preferences.InitDB(serverOptions.SigNoz.SQLStore.SQLxDB()); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := dashboards.InitDB(serverOptions.SigNoz.SQLStore); err != nil {
|
if err := dashboards.InitDB(serverOptions.SigNoz.SQLStore); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -188,6 +186,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
telemetry.GetInstance().SetReader(reader)
|
telemetry.GetInstance().SetReader(reader)
|
||||||
|
preferenceModule := preference.NewAPI(preferencecore.NewPreference(preferencecore.NewStore(serverOptions.SigNoz.SQLStore), preferencetypes.NewDefaultPreferenceMap()))
|
||||||
apiHandler, err := NewAPIHandler(APIHandlerOpts{
|
apiHandler, err := NewAPIHandler(APIHandlerOpts{
|
||||||
Reader: reader,
|
Reader: reader,
|
||||||
SkipConfig: skipConfig,
|
SkipConfig: skipConfig,
|
||||||
@ -205,6 +204,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
|||||||
JWT: serverOptions.Jwt,
|
JWT: serverOptions.Jwt,
|
||||||
AlertmanagerAPI: alertmanager.NewAPI(serverOptions.SigNoz.Alertmanager),
|
AlertmanagerAPI: alertmanager.NewAPI(serverOptions.SigNoz.Alertmanager),
|
||||||
Signoz: serverOptions.SigNoz,
|
Signoz: serverOptions.SigNoz,
|
||||||
|
Preference: preferenceModule,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -66,6 +66,7 @@ func NewSQLMigrationProviderFactories(sqlstore sqlstore.SQLStore) factory.NamedM
|
|||||||
sqlmigration.NewUpdateInvitesFactory(sqlstore),
|
sqlmigration.NewUpdateInvitesFactory(sqlstore),
|
||||||
sqlmigration.NewUpdatePatFactory(sqlstore),
|
sqlmigration.NewUpdatePatFactory(sqlstore),
|
||||||
sqlmigration.NewUpdateAlertmanagerFactory(sqlstore),
|
sqlmigration.NewUpdateAlertmanagerFactory(sqlstore),
|
||||||
|
sqlmigration.NewUpdatePreferencesFactory(sqlstore),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ func (migration *updateAlertmanager) Up(ctx context.Context, db *bun.DB) error {
|
|||||||
err = migration.
|
err = migration.
|
||||||
store.
|
store.
|
||||||
Dialect().
|
Dialect().
|
||||||
UpdatePrimaryKey(ctx, tx, new(existingAlertmanagerConfig), new(newAlertmanagerConfig), func(ctx context.Context) error {
|
UpdatePrimaryKey(ctx, tx, new(existingAlertmanagerConfig), new(newAlertmanagerConfig), OrgReference, func(ctx context.Context) error {
|
||||||
existingAlertmanagerConfigs := make([]*existingAlertmanagerConfig, 0)
|
existingAlertmanagerConfigs := make([]*existingAlertmanagerConfig, 0)
|
||||||
err = tx.
|
err = tx.
|
||||||
NewSelect().
|
NewSelect().
|
||||||
@ -174,7 +174,7 @@ func (migration *updateAlertmanager) Up(ctx context.Context, db *bun.DB) error {
|
|||||||
err = migration.
|
err = migration.
|
||||||
store.
|
store.
|
||||||
Dialect().
|
Dialect().
|
||||||
UpdatePrimaryKey(ctx, tx, new(existingAlertmanagerState), new(newAlertmanagerState), func(ctx context.Context) error {
|
UpdatePrimaryKey(ctx, tx, new(existingAlertmanagerState), new(newAlertmanagerState), OrgReference, func(ctx context.Context) error {
|
||||||
existingAlertmanagerStates := make([]*existingAlertmanagerState, 0)
|
existingAlertmanagerStates := make([]*existingAlertmanagerState, 0)
|
||||||
err = tx.
|
err = tx.
|
||||||
NewSelect().
|
NewSelect().
|
||||||
|
202
pkg/sqlmigration/022_update_preferences.go
Normal file
202
pkg/sqlmigration/022_update_preferences.go
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
package sqlmigration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/migrate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type updatePreferences struct {
|
||||||
|
store sqlstore.SQLStore
|
||||||
|
}
|
||||||
|
|
||||||
|
type existingOrgPreference struct {
|
||||||
|
bun.BaseModel `bun:"table:org_preference"`
|
||||||
|
PreferenceID string `bun:"preference_id,pk,type:text,notnull"`
|
||||||
|
PreferenceValue string `bun:"preference_value,type:text,notnull"`
|
||||||
|
OrgID string `bun:"org_id,pk,type:text,notnull"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type newOrgPreference struct {
|
||||||
|
bun.BaseModel `bun:"table:org_preference_new"`
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type existingUserPreference struct {
|
||||||
|
bun.BaseModel `bun:"table:user_preference"`
|
||||||
|
PreferenceID string `bun:"preference_id,type:text,pk"`
|
||||||
|
PreferenceValue string `bun:"preference_value,type:text"`
|
||||||
|
UserID string `bun:"user_id,type:text,pk"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type newUserPreference struct {
|
||||||
|
bun.BaseModel `bun:"table:user_preference_new"`
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdatePreferencesFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
|
||||||
|
return factory.
|
||||||
|
NewProviderFactory(
|
||||||
|
factory.MustNewName("update_preferences"),
|
||||||
|
func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
|
||||||
|
return newUpdatePreferences(ctx, ps, c, sqlstore)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUpdatePreferences(_ context.Context, _ factory.ProviderSettings, _ Config, store sqlstore.SQLStore) (SQLMigration, error) {
|
||||||
|
return &updatePreferences{store: store}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updatePreferences) Register(migrations *migrate.Migrations) error {
|
||||||
|
if err := migrations.
|
||||||
|
Register(migration.Up, migration.Down); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updatePreferences) Up(ctx context.Context, db *bun.DB) error {
|
||||||
|
tx, err := db.
|
||||||
|
BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
err = migration.
|
||||||
|
store.
|
||||||
|
Dialect().
|
||||||
|
AddPrimaryKey(ctx, tx, new(existingOrgPreference), new(newOrgPreference), OrgReference, func(ctx context.Context) error {
|
||||||
|
existingOrgPreferences := make([]*existingOrgPreference, 0)
|
||||||
|
err = tx.
|
||||||
|
NewSelect().
|
||||||
|
Model(&existingOrgPreferences).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if err != sql.ErrNoRows {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && len(existingOrgPreferences) > 0 {
|
||||||
|
newOrgPreferences := migration.
|
||||||
|
CopyOldOrgPreferencesToNewOrgPreferences(existingOrgPreferences)
|
||||||
|
_, err = tx.
|
||||||
|
NewInsert().
|
||||||
|
Model(&newOrgPreferences).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tableName := tx.Dialect().Tables().Get(reflect.TypeOf(new(existingOrgPreference))).Name
|
||||||
|
_, err = tx.
|
||||||
|
ExecContext(ctx, fmt.Sprintf("CREATE UNIQUE INDEX IF NOT EXISTS %s_unique_idx ON %s (preference_id, org_id)", tableName, fmt.Sprintf("%s_new", tableName)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = migration.
|
||||||
|
store.
|
||||||
|
Dialect().
|
||||||
|
AddPrimaryKey(ctx, tx, new(existingUserPreference), new(newUserPreference), UserReference, func(ctx context.Context) error {
|
||||||
|
existingUserPreferences := make([]*existingUserPreference, 0)
|
||||||
|
err = tx.
|
||||||
|
NewSelect().
|
||||||
|
Model(&existingUserPreferences).
|
||||||
|
Scan(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if err != sql.ErrNoRows {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && len(existingUserPreferences) > 0 {
|
||||||
|
newUserPreferences := migration.
|
||||||
|
CopyOldUserPreferencesToNewUserPreferences(existingUserPreferences)
|
||||||
|
_, err = tx.
|
||||||
|
NewInsert().
|
||||||
|
Model(&newUserPreferences).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tableName := tx.Dialect().Tables().Get(reflect.TypeOf(new(existingUserPreference))).Name
|
||||||
|
_, err = tx.
|
||||||
|
ExecContext(ctx, fmt.Sprintf("CREATE UNIQUE INDEX IF NOT EXISTS %s_unique_idx ON %s (preference_id, user_id)", tableName, fmt.Sprintf("%s_new", tableName)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updatePreferences) Down(context.Context, *bun.DB) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updatePreferences) CopyOldOrgPreferencesToNewOrgPreferences(existingOrgPreferences []*existingOrgPreference) []*newOrgPreference {
|
||||||
|
newOrgPreferences := make([]*newOrgPreference, 0)
|
||||||
|
for _, preference := range existingOrgPreferences {
|
||||||
|
newOrgPreferences = append(newOrgPreferences, &newOrgPreference{
|
||||||
|
Identifiable: types.Identifiable{
|
||||||
|
ID: valuer.GenerateUUID(),
|
||||||
|
},
|
||||||
|
PreferenceID: preference.PreferenceID,
|
||||||
|
PreferenceValue: preference.PreferenceValue,
|
||||||
|
OrgID: preference.OrgID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return newOrgPreferences
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updatePreferences) CopyOldUserPreferencesToNewUserPreferences(existingUserPreferences []*existingUserPreference) []*newUserPreference {
|
||||||
|
newUserPreferences := make([]*newUserPreference, 0)
|
||||||
|
for _, preference := range existingUserPreferences {
|
||||||
|
newUserPreferences = append(newUserPreferences, &newUserPreference{
|
||||||
|
Identifiable: types.Identifiable{
|
||||||
|
ID: valuer.GenerateUUID(),
|
||||||
|
},
|
||||||
|
PreferenceID: preference.PreferenceID,
|
||||||
|
PreferenceValue: preference.PreferenceValue,
|
||||||
|
UserID: preference.UserID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return newUserPreferences
|
||||||
|
}
|
@ -25,6 +25,11 @@ var (
|
|||||||
ErrNoExecute = errors.New("no execute")
|
ErrNoExecute = errors.New("no execute")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
OrgReference = "org"
|
||||||
|
UserReference = "user"
|
||||||
|
)
|
||||||
|
|
||||||
func New(
|
func New(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
settings factory.ProviderSettings,
|
settings factory.ProviderSettings,
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,6 +15,16 @@ var (
|
|||||||
Text = "TEXT"
|
Text = "TEXT"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Org = "org"
|
||||||
|
User = "user"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
OrgReference = `("org_id") REFERENCES "organizations" ("id")`
|
||||||
|
UserReference = `("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE`
|
||||||
|
)
|
||||||
|
|
||||||
type dialect struct {
|
type dialect struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +240,10 @@ func (dialect *dialect) AddNotNullDefaultToColumn(ctx context.Context, bun bun.I
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, cb func(context.Context) error) error {
|
func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, reference string, cb func(context.Context) error) error {
|
||||||
|
if reference == "" {
|
||||||
|
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "cannot run migration without reference")
|
||||||
|
}
|
||||||
oldTableName := bun.Dialect().Tables().Get(reflect.TypeOf(oldModel)).Name
|
oldTableName := bun.Dialect().Tables().Get(reflect.TypeOf(oldModel)).Name
|
||||||
newTableName := bun.Dialect().Tables().Get(reflect.TypeOf(newModel)).Name
|
newTableName := bun.Dialect().Tables().Get(reflect.TypeOf(newModel)).Name
|
||||||
|
|
||||||
@ -241,11 +255,74 @@ func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldMo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fkReference := ""
|
||||||
|
if reference == Org {
|
||||||
|
fkReference = OrgReference
|
||||||
|
} else if reference == User {
|
||||||
|
fkReference = UserReference
|
||||||
|
}
|
||||||
|
|
||||||
_, err = bun.
|
_, err = bun.
|
||||||
NewCreateTable().
|
NewCreateTable().
|
||||||
IfNotExists().
|
IfNotExists().
|
||||||
Model(newModel).
|
Model(newModel).
|
||||||
ForeignKey(`("org_id") REFERENCES "organizations" ("id")`).
|
ForeignKey(fkReference).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cb(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bun.
|
||||||
|
NewDropTable().
|
||||||
|
IfExists().
|
||||||
|
Model(oldModel).
|
||||||
|
Exec(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bun.
|
||||||
|
ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s RENAME TO %s", newTableName, oldTableName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *dialect) AddPrimaryKey(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, reference string, cb func(context.Context) error) error {
|
||||||
|
if reference == "" {
|
||||||
|
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "cannot run migration without reference")
|
||||||
|
}
|
||||||
|
oldTableName := bun.Dialect().Tables().Get(reflect.TypeOf(oldModel)).Name
|
||||||
|
newTableName := bun.Dialect().Tables().Get(reflect.TypeOf(newModel)).Name
|
||||||
|
|
||||||
|
identityExists, err := dialect.ColumnExists(ctx, bun, oldTableName, Identity)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if identityExists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fkReference := ""
|
||||||
|
if reference == Org {
|
||||||
|
fkReference = OrgReference
|
||||||
|
} else if reference == User {
|
||||||
|
fkReference = UserReference
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bun.
|
||||||
|
NewCreateTable().
|
||||||
|
IfNotExists().
|
||||||
|
Model(newModel).
|
||||||
|
ForeignKey(fkReference).
|
||||||
Exec(ctx)
|
Exec(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -44,5 +44,6 @@ type SQLDialect interface {
|
|||||||
ColumnExists(context.Context, bun.IDB, string, string) (bool, error)
|
ColumnExists(context.Context, bun.IDB, string, string) (bool, error)
|
||||||
RenameColumn(context.Context, bun.IDB, string, string, string) (bool, error)
|
RenameColumn(context.Context, bun.IDB, string, string, string) (bool, error)
|
||||||
RenameTableAndModifyModel(context.Context, bun.IDB, interface{}, interface{}, func(context.Context) error) error
|
RenameTableAndModifyModel(context.Context, bun.IDB, interface{}, interface{}, func(context.Context) error) error
|
||||||
UpdatePrimaryKey(context.Context, bun.IDB, interface{}, interface{}, func(context.Context) error) error
|
UpdatePrimaryKey(context.Context, bun.IDB, interface{}, interface{}, string, func(context.Context) error) error
|
||||||
|
AddPrimaryKey(context.Context, bun.IDB, interface{}, interface{}, string, func(context.Context) error) error
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,11 @@ func (dialect *dialect) AddNotNullDefaultToColumn(ctx context.Context, bun bun.I
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, cb func(context.Context) error) error {
|
func (dialect *dialect) UpdatePrimaryKey(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, reference string, cb func(context.Context) error) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *dialect) AddPrimaryKey(ctx context.Context, bun bun.IDB, oldModel interface{}, newModel interface{}, reference string, cb func(context.Context) error) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/uptrace/bun"
|
|
||||||
|
|
||||||
// on_delete:CASCADE,on_update:CASCADE not working
|
|
||||||
type UserPreference struct {
|
|
||||||
bun.BaseModel `bun:"table:user_preference"`
|
|
||||||
|
|
||||||
PreferenceID string `bun:"preference_id,type:text,pk"`
|
|
||||||
PreferenceValue string `bun:"preference_value,type:text"`
|
|
||||||
UserID string `bun:"user_id,type:text,pk"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// on_delete:CASCADE,on_update:CASCADE not working
|
|
||||||
type OrgPreference struct {
|
|
||||||
bun.BaseModel `bun:"table:org_preference"`
|
|
||||||
|
|
||||||
PreferenceID string `bun:"preference_id,pk,type:text,notnull"`
|
|
||||||
PreferenceValue string `bun:"preference_value,type:text,notnull"`
|
|
||||||
OrgID string `bun:"org_id,pk,type:text,notnull"`
|
|
||||||
}
|
|
290
pkg/types/preferencetypes/preference.go
Normal file
290
pkg/types/preferencetypes/preference.go
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
package preferencetypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
|
"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 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultPreferenceMap() map[string]Preference {
|
||||||
|
return map[string]Preference{
|
||||||
|
"ORG_ONBOARDING": {
|
||||||
|
Key: "ORG_ONBOARDING",
|
||||||
|
Name: "Organisation Onboarding",
|
||||||
|
Description: "Organisation Onboarding",
|
||||||
|
ValueType: "boolean",
|
||||||
|
DefaultValue: false,
|
||||||
|
AllowedValues: []interface{}{true, false},
|
||||||
|
IsDiscreteValues: true,
|
||||||
|
AllowedScopes: []string{"org"},
|
||||||
|
},
|
||||||
|
"WELCOME_CHECKLIST_DO_LATER": {
|
||||||
|
Key: "WELCOME_CHECKLIST_DO_LATER",
|
||||||
|
Name: "Welcome Checklist Do Later",
|
||||||
|
Description: "Welcome Checklist Do Later",
|
||||||
|
ValueType: "boolean",
|
||||||
|
DefaultValue: false,
|
||||||
|
AllowedValues: []interface{}{true, false},
|
||||||
|
IsDiscreteValues: true,
|
||||||
|
AllowedScopes: []string{"user"},
|
||||||
|
},
|
||||||
|
"WELCOME_CHECKLIST_SEND_LOGS_SKIPPED": {
|
||||||
|
Key: "WELCOME_CHECKLIST_SEND_LOGS_SKIPPED",
|
||||||
|
Name: "Welcome Checklist Send Logs Skipped",
|
||||||
|
Description: "Welcome Checklist Send Logs Skipped",
|
||||||
|
ValueType: "boolean",
|
||||||
|
DefaultValue: false,
|
||||||
|
AllowedValues: []interface{}{true, false},
|
||||||
|
IsDiscreteValues: true,
|
||||||
|
AllowedScopes: []string{"user"},
|
||||||
|
},
|
||||||
|
"WELCOME_CHECKLIST_SEND_TRACES_SKIPPED": {
|
||||||
|
Key: "WELCOME_CHECKLIST_SEND_TRACES_SKIPPED",
|
||||||
|
Name: "Welcome Checklist Send Traces Skipped",
|
||||||
|
Description: "Welcome Checklist Send Traces Skipped",
|
||||||
|
ValueType: "boolean",
|
||||||
|
DefaultValue: false,
|
||||||
|
AllowedValues: []interface{}{true, false},
|
||||||
|
IsDiscreteValues: true,
|
||||||
|
AllowedScopes: []string{"user"},
|
||||||
|
},
|
||||||
|
"WELCOME_CHECKLIST_SEND_INFRA_METRICS_SKIPPED": {
|
||||||
|
Key: "WELCOME_CHECKLIST_SEND_INFRA_METRICS_SKIPPED",
|
||||||
|
Name: "Welcome Checklist Send Infra Metrics Skipped",
|
||||||
|
Description: "Welcome Checklist Send Infra Metrics Skipped",
|
||||||
|
ValueType: "boolean",
|
||||||
|
DefaultValue: false,
|
||||||
|
AllowedValues: []interface{}{true, false},
|
||||||
|
IsDiscreteValues: true,
|
||||||
|
AllowedScopes: []string{"user"},
|
||||||
|
},
|
||||||
|
"WELCOME_CHECKLIST_SETUP_DASHBOARDS_SKIPPED": {
|
||||||
|
Key: "WELCOME_CHECKLIST_SETUP_DASHBOARDS_SKIPPED",
|
||||||
|
Name: "Welcome Checklist Setup Dashboards Skipped",
|
||||||
|
Description: "Welcome Checklist Setup Dashboards Skipped",
|
||||||
|
ValueType: "boolean",
|
||||||
|
DefaultValue: false,
|
||||||
|
AllowedValues: []interface{}{true, false},
|
||||||
|
IsDiscreteValues: true,
|
||||||
|
AllowedScopes: []string{"user"},
|
||||||
|
},
|
||||||
|
"WELCOME_CHECKLIST_SETUP_ALERTS_SKIPPED": {
|
||||||
|
Key: "WELCOME_CHECKLIST_SETUP_ALERTS_SKIPPED",
|
||||||
|
Name: "Welcome Checklist Setup Alerts Skipped",
|
||||||
|
Description: "Welcome Checklist Setup Alerts Skipped",
|
||||||
|
ValueType: "boolean",
|
||||||
|
DefaultValue: false,
|
||||||
|
AllowedValues: []interface{}{true, false},
|
||||||
|
IsDiscreteValues: true,
|
||||||
|
AllowedScopes: []string{"user"},
|
||||||
|
},
|
||||||
|
"WELCOME_CHECKLIST_SETUP_SAVED_VIEW_SKIPPED": {
|
||||||
|
Key: "WELCOME_CHECKLIST_SETUP_SAVED_VIEW_SKIPPED",
|
||||||
|
Name: "Welcome Checklist Setup Saved View Skipped",
|
||||||
|
Description: "Welcome Checklist Setup Saved View Skipped",
|
||||||
|
ValueType: "boolean",
|
||||||
|
DefaultValue: false,
|
||||||
|
AllowedValues: []interface{}{true, false},
|
||||||
|
IsDiscreteValues: true,
|
||||||
|
AllowedScopes: []string{"user"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Preference) ErrorValueTypeMismatch() error {
|
||||||
|
return errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, fmt.Sprintf("the preference value is not of expected type: %s", p.ValueType))
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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, fmt.Sprintf("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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, fmt.Sprintf("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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreferenceStore interface {
|
||||||
|
GetOrgPreference(context.Context, string, string) (*StorableOrgPreference, error)
|
||||||
|
GetAllOrgPreferences(context.Context, string) ([]*StorableOrgPreference, error)
|
||||||
|
UpsertOrgPreference(context.Context, *StorableOrgPreference) error
|
||||||
|
GetUserPreference(context.Context, string, string) (*StorableUserPreference, error)
|
||||||
|
GetAllUserPreferences(context.Context, string) ([]*StorableUserPreference, error)
|
||||||
|
UpsertUserPreference(context.Context, *StorableUserPreference) error
|
||||||
|
}
|
23
pkg/types/preferencetypes/value.go
Normal file
23
pkg/types/preferencetypes/value.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package preferencetypes
|
||||||
|
|
||||||
|
const (
|
||||||
|
PreferenceValueTypeInteger string = "integer"
|
||||||
|
PreferenceValueTypeFloat string = "float"
|
||||||
|
PreferenceValueTypeString string = "string"
|
||||||
|
PreferenceValueTypeBoolean string = "boolean"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OrgAllowedScope string = "org"
|
||||||
|
UserAllowedScope string = "user"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Range struct {
|
||||||
|
Min int64 `json:"min"`
|
||||||
|
Max int64 `json:"max"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreferenceWithValue struct {
|
||||||
|
Preference
|
||||||
|
Value interface{} `json:"value"`
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user