settings

package
v0.0.0-...-d09923a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 5, 2026 License: LGPL-2.1 Imports: 9 Imported by: 0

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

View Source
const (
	LevelTenant = "tenant"
	LevelOrg    = "org"
	LevelUser   = "user"
)

Setting levels. A level says which kind of owner a setting belongs to.

View Source
const UpdateSettingsRoutePath = "/settings/:level"

UpdateSettingsRoutePath is mounted under the module's own `/v1/{module_key}` group.

Variables

This section is empty.

Functions

func IsKnownLevel

func IsKnownLevel(level string) bool

IsKnownLevel reports whether level names one of the three setting levels.

func OptionalOrgId

func OptionalOrgId(raw *string) *model.Id

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

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

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL