flags

package
v1.15.0 Latest Latest
Warning

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

Go to latest
Published: Nov 12, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package flags turns features on for some users and not others, decided on the server: a kill switch for each feature, lists of users and teams who get it first, and a rollout to a share of users, teams or visitors that grows without anyone losing it.

f, err := flags.New(db, flags.Options{Subject: flags.AccountsSubject},
	flags.Flag{Name: "new-editor", Description: "The editor with live previews"},
)

if f.Enabled(c, "new-editor") { ... }

Flags are kept in the database, so they change without a deploy, and read from memory, so checking one costs nothing. f.Admin() is a resource for the admin package's screens, to change them.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Flag

type Flag struct {
	// Name identifies the flag in code, such as "new-editor".
	Name string `` /* 163-byte string literal not displayed */
	// Description says what it turns on, for whoever changes it.
	Description string `json:"description" admin:"list,search" validate:"max=500"`
	// On is the kill switch: off, nobody gets the feature.
	On bool `json:"on" admin:"list,filter" description:"Off, nobody gets it, whatever else is set."`
	// Percent of the subjects counted By get it, 0 to 100. Raising it keeps
	// those who had it: each subject's place is fixed by a hash.
	Percent int `json:"percent" admin:"list" validate:"min=0,max=100" description:"The share of users, teams or visitors who get it."`
	// By is what Percent counts: "user", "team" or "visitor", a browser
	// with a cookie.
	By string `json:"by" admin:"list" validate:"oneof=user|team|visitor" label:"Counted by"`
	// Users and Teams get it whatever the percentage, as IDs separated by
	// commas: the team that built it, the customers who asked for it.
	Users string `json:"users" admin:"textarea" description:"User IDs who always get it, separated by commas."`
	Teams string `json:"teams" admin:"textarea" description:"Team IDs who always get it, separated by commas."`
}

Flag is a feature and who gets it.

func (Flag) Decide

func (fl Flag) Decide(s Subject, visitor string) bool

Decide says whether s gets the flag: never when it's off; always when s is in its users or teams; otherwise when s's place, a hash of the flag's name and s's ID counted By, is within Percent. visitor is the visitor's ID, for flags counted by visitor.

type Flags

type Flags struct {
	// contains filtered or unexported fields
}

Flags are an app's feature flags. Add them to the app with kit.WithPlugins, so they're read when it starts and kept fresh.

func New

func New(db *sql.DB, opts Options, defined ...Flag) (*Flags, error)

New returns flags kept in db, SQLite or PostgreSQL, with the flags the code knows about. Those not in the database yet are added, off, when the app starts; flags already there keep how they were set.

func (*Flags) Admin

func (f *Flags) Admin(opts ...admin.Option) admin.Res

Admin is a resource for the admin package's screens, to see and change the flags. Changes apply in this process at once.

admin.New(admin.Options{Authorize: onlyAdmins}, f.Admin())

func (*Flags) Enabled

func (f *Flags) Enabled(c *kit.Ctx, name string) bool

Enabled reports whether the request's subject gets the flag. A flag that doesn't exist is off. Decisions are the same for a subject on every request, until the flag changes.

func (*Flags) For

func (f *Flags) For(c *kit.Ctx, names ...string) map[string]bool

For returns the decisions for these flags, such as for a layout's load to send the page: data.flags["new-editor"].

func (*Flags) Get

func (f *Flags) Get(name string) (Flag, bool)

Get returns a flag, and whether there is one.

func (*Flags) List

func (f *Flags) List() []Flag

List returns every flag, by name.

func (*Flags) Migrate

func (f *Flags) Migrate(ctx context.Context) error

Migrate creates the flags' table, and adds the flags the code defines that aren't in it. The plugin runs it when the app starts.

func (*Flags) Name

func (f *Flags) Name() string

Name implements kit.Plugin.

func (*Flags) Reload

func (f *Flags) Reload(ctx context.Context) error

Reload reads the flags from the database now.

func (*Flags) Remove

func (f *Flags) Remove(ctx context.Context, name string) error

Remove deletes a flag. Code that still checks it gets false.

func (*Flags) Set

func (f *Flags) Set(ctx context.Context, fl Flag) error

Set saves a flag, creating it if it's new, and applies it in this process at once; others pick it up within Options.Refresh.

func (*Flags) Setup

func (f *Flags) Setup(s *kit.Setup) error

Setup implements kit.Plugin: the flags are read when the app starts, and again every Options.Refresh.

type Options

type Options struct {
	// Subject says who a request is for. Without it, only visitors are
	// known, by their cookie.
	Subject func(c *kit.Ctx) Subject
	// Refresh is how often each process reads changes others made, such
	// as from the admin screens on another server. Default: 10 seconds.
	Refresh time.Duration
	// Cookie names the visitor cookie for flags counted by visitor.
	// Default: "tern_visitor".
	Cookie string
}

Options configure flags.

type Subject

type Subject struct {
	User string
	Team string
}

Subject is who a request is for. Empty fields are unknown.

func AccountsSubject

func AccountsSubject(c *kit.Ctx) Subject

AccountsSubject is the signed-in user and their current team, from the accounts package. Add accounts' plugin before the flags'.

Jump to

Keyboard shortcuts

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