Documentation
¶
Overview ¶
Package settings is the shared contract every feature module writes its settings through.
A module owns the update endpoint for its own settings: it validates the items against its own schema and its own data, then persists them through SettingsService. The Settings module implements the service, but core cannot name it (settings depends on core), so apptrait registers the implementation at runtime -- the same seam requestguard uses.
Index ¶
- Constants
- func IsKnownLevel(level string) bool
- func OptionalOrgId(raw *string) *model.Id
- func UnknownSettingError(name string) *ft.ClientErrorItem
- func UpdateSettingsHandler(...) echo.HandlerFunc
- func ValidateItems(schema *dmodel.ModelSchema, items []SetSettingItem) ft.ClientErrors
- func ValidateValue(field *dmodel.ModelField, value any) *ft.ClientErrorItem
- type ModuleSettingsWriter
- type OrgSettingsReader
- type SetSettingItem
- type SetSettingsCommand
- type SetSettingsResult
- type SetSettingsResultData
- type SettingsRule
- type SettingsService
- type UpdateSettingItemDto
- type UpdateSettingsCommand
- type UpdateSettingsRequest
- type UpdateSettingsResponse
Constants ¶
const ( LevelTenant = "tenant" LevelOrg = "org" LevelUser = "user" )
Setting levels. A level says which kind of owner a setting belongs to.
const UpdateSettingsRoutePath = "/settings/:level"
UpdateSettingsRoutePath is mounted under the module's own `/v1/{module_key}` group.
Variables ¶
This section is empty.
Functions ¶
func IsKnownLevel ¶
IsKnownLevel reports whether level names one of the three setting levels.
func OptionalOrgId ¶
OptionalOrgId reads an optional org_id parameter; absent or blank both mean "not named".
func UnknownSettingError ¶
func UnknownSettingError(name string) *ft.ClientErrorItem
UnknownSettingError is the caller's error for a name no schema declares, not a server fault.
func UpdateSettingsHandler ¶
func UpdateSettingsHandler( updateFn func(corectx.Context, UpdateSettingsCommand) (*SetSettingsResult, error), ) echo.HandlerFunc
UpdateSettingsHandler is a module's settings update route, mounted as `route.PATCH(UpdateSettingsRoutePath, UpdateSettingsHandler(svc.UpdateSettings), m.UserSmokeAuthz())`.
func ValidateItems ¶
func ValidateItems(schema *dmodel.ModelSchema, items []SetSettingItem) ft.ClientErrors
ValidateItems checks every item against the schema the module registered for the level: the name must be declared and the value must fit its field. It is the schema half of a module's validation; the Settings module runs the same check again when it persists.
func ValidateValue ¶
func ValidateValue(field *dmodel.ModelField, value any) *ft.ClientErrorItem
ValidateValue checks one value against its declared field. A nil value clears the setting back to its declared default, which every field accepts.
Types ¶
type ModuleSettingsWriter ¶
type ModuleSettingsWriter struct {
// contains filtered or unexported fields
}
ModuleSettingsWriter is the shared body of every module's settings update: authorize, refuse a level the module declares nothing at, validate the items against the module's schema, run the module's rules, then persist through SettingsService.
func NewModuleSettingsWriter ¶
func NewModuleSettingsWriter( moduleKey string, schemas map[string]*dmodel.ModelSchema, service SettingsService, rules ...SettingsRule, ) *ModuleSettingsWriter
NewModuleSettingsWriter takes the schemas by level, the same ones the module registers with the Settings module, so the two validations can never disagree on what a setting is.
func (*ModuleSettingsWriter) Update ¶
func (this *ModuleSettingsWriter) Update(ctx corectx.Context, cmd UpdateSettingsCommand) (*SetSettingsResult, error)
type OrgSettingsReader ¶
type OrgSettingsReader interface {
GetOrgSettingValues(ctx corectx.Context, orgId model.Id, moduleKey string) (map[string]any, error)
}
OrgSettingsReader reads the values one organization stores for a module, without asserting a permission and without guessing the organization from the request: a module reads its own settings (a sequence definition id, say) on behalf of a caller who need not hold settings access. A missing value is absent from the map; a failed read is an error, never an empty map, so "could not read" is never taken for "not set". The Settings module provides the one implementation.
type SetSettingItem ¶
type SetSettingItem struct {
Name string
Value any
// AllowOverride, when set, changes whether owners below the tenant may keep their own value.
//
// Only a tenant owner may send it: the flag is what locks the levels below, so an org or user
// setting it would be escalating their own privilege. Nil leaves the stored policy alone,
// which is what every non-tenant caller sends.
AllowOverride *bool
}
type SetSettingsCommand ¶
type SetSettingsCommand struct {
ModuleKey string
Items []SetSettingItem
// OrgId names the organization an org-level write acts for, as org_id does on every
// org-scoped resource. Nil keeps the owner the request implies (service principal's org, org
// unit's org, sole membership); other levels ignore it.
OrgId *model.Id
}
SetSettingsCommand writes a partial set of changed items for one owner at one level.
Only the items the caller actually changed belong here. An absent item is left untouched rather than cleared, and — because writes are last-write-wins with no version check — submitting an unchanged item is how a concurrent edit to a field this caller never touched gets clobbered.
type SetSettingsResult ¶
type SetSettingsResult = dyn.OpResult[SetSettingsResultData]
type SetSettingsResultData ¶
type SetSettingsResultData struct {
// Updated counts the rows written, including the children an enforced tenant setting fanned
// out onto.
Updated int
}
type SettingsRule ¶
type SettingsRule func(ctx corectx.Context, level string, items []SetSettingItem) (ft.ClientErrors, error)
SettingsRule is a module's own check on a write, beyond what its schema can say -- typically one that reads the module's data, such as "this language is enabled". It runs only after the items passed the schema, so it may assume every name is declared and every value well-typed.
type SettingsService ¶
type SettingsService interface {
SetSettings(ctx corectx.Context, level string, cmd SetSettingsCommand) (*SetSettingsResult, error)
}
SettingsService persists a module's settings at one level, for the owner the acting user stands for at that level.
The implementation authorizes exactly as the Settings module's application services do: a tenant or org write requires update on settings_record, a user write is the actor's own.
type UpdateSettingItemDto ¶
type UpdateSettingItemDto struct {
Name string `json:"name"`
Value any `json:"value"`
// AllowOverride is accepted only from a tenant-level caller; see SetSettingItem. Absent leaves
// the stored policy untouched, so an org or user PATCH never has to send it.
AllowOverride *bool `json:"allow_override,omitempty"`
}
type UpdateSettingsCommand ¶
type UpdateSettingsCommand struct {
Level string
Items []SetSettingItem
// OrgId is the org an org-level write acts for; see SetSettingsCommand.OrgId.
OrgId *model.Id
}
UpdateSettingsCommand is what a module's own update endpoint receives. The module key is not in it: the endpoint belongs to one module, which names itself.
type UpdateSettingsRequest ¶
type UpdateSettingsRequest struct {
Level string `param:"level" json:"-"`
Items []UpdateSettingItemDto `json:"items"`
// OrgId names the org an org-level write acts for, as on every org-scoped resource.
OrgId *string `json:"org_id,omitempty"`
}
UpdateSettingsRequest is the body of every module's `PATCH /v1/{module_key}/settings/:level`. One shape for every module is what lets a frontend settings widget derive the endpoint from its module key alone.
It carries only the items the caller changed. An absent item is left untouched rather than cleared, and — because writes are last-write-wins with no version check — sending an unchanged item is how a concurrent edit to a field this caller never touched gets clobbered.
func (UpdateSettingsRequest) ToCommand ¶
func (this UpdateSettingsRequest) ToCommand() UpdateSettingsCommand
type UpdateSettingsResponse ¶
type UpdateSettingsResponse struct {
// Updated counts the rows written, including the children an enforced tenant setting fanned
// out onto, so a tenant admin can see that the change reached more than one row.
Updated int `json:"updated"`
}
func NewUpdateSettingsResponse ¶
func NewUpdateSettingsResponse(data SetSettingsResultData) UpdateSettingsResponse