Documentation ¶
Overview ¶
Package unleash is a client library for connecting to an Unleash feature toggle server.
See https://github.com/Unleash/unleash for more information.
Basics ¶
The API is very simple. The main functions of interest are Initialize and IsEnabled. Calling Initialize will create a default client and if a listener is supplied, it will start the sync loop. Internally the client consists of two components. The first is the repository which runs in a separate Go routine and polls the server to get the latest feature toggles. Once the feature toggles are fetched, they are stored by sending the data to an instance of the Storage interface which is responsible for storing the data both in memory and also persisting it somewhere. The second component is the metrics component which is responsible for tracking how often features were queried and whether or not they were enabled. The metrics components also runs in a separate Go routine and will occasionally upload the latest metrics to the Unleash server. The client struct creates a set of channels that it passes to both of the above components and it uses those for communicating asynchronously. It is important to ensure that these channels get regularly drained to avoid blocking those Go routines. There are two ways this can be done.
Using the Listener Interfaces ¶
The first and perhaps simplest way to "drive" the synchronization loop in the client is to provide a type that implements one or more of the listener interfaces. There are 3 interfaces and you can choose which ones you should implement:
- ErrorListener
- RepositoryListener
- MetricsListener
If you are only interesting in tracking errors and warnings and don't care about any of the other signals, then you only need to implement the ErrorListener and pass this instance to WithListener(). The DebugListener shows an example of implementing all of the listeners in a single type.
Reading the channels directly ¶
If you would prefer to have control over draining the channels yourself, then you must not call WithListener(). Instead, you should read all of the channels continuously inside a select. The WithInstance example shows how to do this. Note that all channels must be drained, even if you are not interested in the result.
Examples ¶
The following examples show how to use the client in different scenarios.
Example (CustomStrategy) ¶
ExampleCustomStrategy demonstrates using a custom strategy.
package main import ( "fmt" "github.com/akuracy/unleash-client-go/v3" "github.com/akuracy/unleash-client-go/v3/context" "strings" "time" ) type ActiveForUserWithEmailStrategy struct{} func (s ActiveForUserWithEmailStrategy) Name() string { return "ActiveForUserWithEmail" } func (s ActiveForUserWithEmailStrategy) IsEnabled(params map[string]interface{}, ctx *context.Context) bool { if ctx == nil { return false } value, found := params["emails"] if !found { return false } emails, ok := value.(string) if !ok { return false } for _, e := range strings.Split(emails, ",") { if e == ctx.Properties["emails"] { return true } } return false } // ExampleCustomStrategy demonstrates using a custom strategy. func main() { unleash.Initialize( unleash.WithListener(&unleash.DebugListener{}), unleash.WithAppName("my-application"), unleash.WithUrl("https://unleash.herokuapp.com/api/"), unleash.WithRefreshInterval(5*time.Second), unleash.WithMetricsInterval(5*time.Second), unleash.WithStrategies(&ActiveForUserWithEmailStrategy{}), ) ctx := context.Context{ Properties: map[string]string{ "emails": "example@example.com", }, } timer := time.NewTimer(1 * time.Second) for { <-timer.C enabled := unleash.IsEnabled("unleash.me", unleash.WithContext(ctx)) fmt.Printf("feature is enabled? %v\n", enabled) timer.Reset(1 * time.Second) } }
Output:
Example (FallbackFunc) ¶
ExampleFallbackFunc demonstrates how to specify a fallback function.
package main import ( "fmt" "github.com/akuracy/unleash-client-go/v3" "github.com/akuracy/unleash-client-go/v3/context" "time" ) const MissingFeature = "does_not_exist" // ExampleFallbackFunc demonstrates how to specify a fallback function. func main() { unleash.Initialize( unleash.WithListener(&unleash.DebugListener{}), unleash.WithAppName("my-application"), unleash.WithUrl("http://unleash.herokuapp.com/api/"), ) fallback := func(feature string, ctx *context.Context) bool { return feature == MissingFeature } timer := time.NewTimer(1 * time.Second) for { <-timer.C isEnabled := unleash.IsEnabled(MissingFeature, unleash.WithFallbackFunc(fallback)) fmt.Printf("'%s' enabled? %v\n", PropertyName, isEnabled) timer.Reset(1 * time.Second) } }
Output:
Example (SimpleUsage) ¶
ExampleSimpleUsage demonstrates the simplest way to use the unleash client.
package main import ( "fmt" "github.com/akuracy/unleash-client-go/v3" "time" ) const PropertyName = "eid.enabled" // ExampleSimpleUsage demonstrates the simplest way to use the unleash client. func main() { unleash.Initialize( unleash.WithListener(&unleash.DebugListener{}), unleash.WithAppName("my-application"), unleash.WithUrl("http://unleash.herokuapp.com/api/"), ) timer := time.NewTimer(1 * time.Second) for { <-timer.C fmt.Printf("'%s' enabled? %v\n", PropertyName, unleash.IsEnabled(PropertyName)) timer.Reset(1 * time.Second) } }
Output:
Example (WithInstance) ¶
ExampleWithInstance demonstrates how to create the client manually instead of using the default client. It also shows how to run the event loop manually.
package main import ( "fmt" "github.com/akuracy/unleash-client-go/v3" "time" ) // Sync runs the client event loop. All of the channels must be read to avoid blocking the // client. func Sync(client *unleash.Client) { timer := time.NewTimer(1 * time.Second) for { select { case e := <-client.Errors(): fmt.Printf("ERROR: %v\n", e) case w := <-client.Warnings(): fmt.Printf("WARNING: %v\n", w) case <-client.Ready(): fmt.Printf("READY\n") case m := <-client.Count(): fmt.Printf("COUNT: %+v\n", m) case md := <-client.Sent(): fmt.Printf("SENT: %+v\n", md) case cd := <-client.Registered(): fmt.Printf("REGISTERED: %+v\n", cd) case <-timer.C: fmt.Printf("ISENABLED: %v\n", client.IsEnabled("eid.enabled")) timer.Reset(1 * time.Second) } } } // ExampleWithInstance demonstrates how to create the client manually instead of using the default client. // It also shows how to run the event loop manually. func main() { // Create the client with the desired options client, err := unleash.NewClient( unleash.WithAppName("my-application"), unleash.WithUrl("http://unleash.herokuapp.com/api/"), ) if err != nil { fmt.Printf("ERROR: Starting client: %v", err) return } Sync(client) }
Output:
Index ¶
- func Close() error
- func Initialize(options ...ConfigOption) (err error)
- func IsEnabled(feature string, options ...FeatureOption) bool
- func WaitForReady()
- type Client
- func (uc *Client) Close() error
- func (uc *Client) Count() <-chan metric
- func (uc *Client) Errors() <-chan error
- func (uc *Client) IsEnabled(feature string, options ...FeatureOption) (enabled bool)
- func (uc *Client) ListFeatures() []api.Feature
- func (uc *Client) Ready() <-chan bool
- func (uc *Client) Registered() <-chan ClientData
- func (uc *Client) Sent() <-chan MetricsData
- func (uc *Client) WaitForReady()
- func (uc *Client) Warnings() <-chan error
- type ClientData
- type ConfigOption
- func WithAppName(appName string) ConfigOption
- func WithBackupPath(backupPath string) ConfigOption
- func WithCustomHeaders(headers http.Header) ConfigOption
- func WithDisableMetrics(disableMetrics bool) ConfigOption
- func WithEnvironment(env string) ConfigOption
- func WithHttpClient(client *http.Client) ConfigOption
- func WithInstanceId(instanceId string) ConfigOption
- func WithListener(listener interface{}) ConfigOption
- func WithMetricsInterval(metricsInterval time.Duration) ConfigOption
- func WithRefreshInterval(refreshInterval time.Duration) ConfigOption
- func WithStorage(storage Storage) ConfigOption
- func WithStrategies(strategies ...strategy.Strategy) ConfigOption
- func WithUrl(url string) ConfigOption
- type DebugListener
- type ErrorListener
- type FallbackFunc
- type FeatureOption
- type MetricListener
- type MetricsData
- type RepositoryListener
- type Storage
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Initialize ¶
func Initialize(options ...ConfigOption) (err error)
Initialize will specify the options to be used by the default client.
func IsEnabled ¶
func IsEnabled(feature string, options ...FeatureOption) bool
IsEnabled queries the default client whether or not the specified feature is enabled or not.
func WaitForReady ¶
func WaitForReady()
WaitForReady will block until the default client is ready or return immediately.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a structure representing an API client of an Unleash server.
func NewClient ¶
func NewClient(options ...ConfigOption) (*Client, error)
NewClient creates a new client instance with the given options.
func (*Client) Count ¶
func (uc *Client) Count() <-chan metric
Count returns the count channel which gives an update when a toggle has been queried.
func (*Client) IsEnabled ¶
func (uc *Client) IsEnabled(feature string, options ...FeatureOption) (enabled bool)
IsEnabled queries whether the specified feature is enabled or not.
It is safe to call this method from multiple goroutines concurrently.
func (*Client) ListFeatures ¶
ListFeatures returns all available features toggles.
func (*Client) Ready ¶
Ready returns the ready channel for the client. A value will be available on the channel when the feature toggles have been loaded from the Unleash server.
func (*Client) Registered ¶
func (uc *Client) Registered() <-chan ClientData
Registered returns the registered signal indicating that the client has successfully connected to the metrics service.
func (*Client) Sent ¶
func (uc *Client) Sent() <-chan MetricsData
Sent returns the sent channel which receives data whenever the client has successfully sent metrics to the metrics service.
func (*Client) WaitForReady ¶
func (uc *Client) WaitForReady()
WaitForReady will block until the client has loaded the feature toggles from the Unleash server. It will return immediately if the toggles have already been loaded,
It is safe to call this method from multiple goroutines concurrently.
type ClientData ¶
type ClientData struct { // AppName is the name of the application. AppName string `json:"appName"` // InstanceID is the instance identifier. InstanceID string `json:"instanceId"` // Optional field that describes the sdk version (name:version) SDKVersion string `json:"sdkVersion"` // Strategies is a list of names of the strategies supported by the client. Strategies []string `json:"strategies"` // Started indicates the time at which the client was created. Started time.Time `json:"started"` // Interval specifies the time interval (in ms) that the client is using for refreshing // feature toggles. Interval int64 `json:"interval"` }
ClientData represents the data sent to the unleash during registration.
type ConfigOption ¶
type ConfigOption func(*configOption)
ConfigOption represents a option for configuring the client.
func WithAppName ¶
func WithAppName(appName string) ConfigOption
WithAppName specifies the name of the application.
func WithBackupPath ¶
func WithBackupPath(backupPath string) ConfigOption
WithBackupPath specifies the path that is passed to the storage implementation for storing the feature toggles locally.
func WithCustomHeaders ¶
func WithCustomHeaders(headers http.Header) ConfigOption
WithCustomHeaders specifies any custom headers that should be sent along with requests to the server.
func WithDisableMetrics ¶
func WithDisableMetrics(disableMetrics bool) ConfigOption
WithDisabledMetrics specifies that the client should not log metrics to the unleash server.
func WithEnvironment ¶
func WithEnvironment(env string) ConfigOption
WithEnvironment specifies the environment
func WithHttpClient ¶
func WithHttpClient(client *http.Client) ConfigOption
WithHttpClient specifies which HttpClient the client should use for making requests to the server.
func WithInstanceId ¶
func WithInstanceId(instanceId string) ConfigOption
WithInstanceId specifies the instance identifier of the current instance. If not provided, one will be generated based on various parameters such as current user and hostname.
func WithListener ¶
func WithListener(listener interface{}) ConfigOption
WithListener allows users to register a type that implements one or more of the listener interfaces. If no listener is registered then the user is responsible for draining the various channels on the client. Failure to do so will stop the client from working as the worker routines will be blocked.
func WithMetricsInterval ¶
func WithMetricsInterval(metricsInterval time.Duration) ConfigOption
WithMetricsInterval specifies the time interval with which the client should upload the metrics data to the unleash server.
func WithRefreshInterval ¶
func WithRefreshInterval(refreshInterval time.Duration) ConfigOption
WithRefreshInterval specifies the time interval with which the client should sync the feature toggles from the unleash server.
func WithStorage ¶
func WithStorage(storage Storage) ConfigOption
WithStorage specifies which storage implementation the repository should use for storing feature toggles.
func WithStrategies ¶
func WithStrategies(strategies ...strategy.Strategy) ConfigOption
WithStrategies specifies which strategies (in addition to the defaults) should be used by the client.
func WithUrl ¶
func WithUrl(url string) ConfigOption
WithUrl specifies the url of the unleash server the user is connecting to.
type DebugListener ¶
type DebugListener struct{}
DebugListener is an implementation of all of the listener interfaces that simply logs debug info to stdout. It is meant for debugging purposes and an example of implementing the listener interfaces.
func (DebugListener) OnCount ¶
func (l DebugListener) OnCount(name string, enabled bool)
OnCount prints to the console when the feature is queried.
func (DebugListener) OnReady ¶
func (l DebugListener) OnReady()
OnReady prints to the console when the repository is ready.
func (DebugListener) OnRegistered ¶
func (l DebugListener) OnRegistered(payload ClientData)
OnRegistered prints to the console when the client has registered.
func (DebugListener) OnSent ¶
func (l DebugListener) OnSent(payload MetricsData)
OnSent prints to the console when the server has uploaded metrics.
func (DebugListener) OnWarning ¶
func (l DebugListener) OnWarning(warning error)
OnWarning prints out warning.
type ErrorListener ¶
type ErrorListener interface { // OnError is called whenever the client experiences an error. OnError(error) // OnWarning is called whenever the client experiences a warning. OnWarning(error) }
ErrorListener defines an interface that be implemented in order to receive errors and warnings from the client.
type FallbackFunc ¶
FallbackFunc represents a function to be called if the feature is not found.
type FeatureOption ¶
type FeatureOption func(*featureOption)
FeatureOption provides options for querying if a feature is enabled or not.
func WithContext ¶
func WithContext(ctx context.Context) FeatureOption
WithContext allows the user to provide a context that will be passed into the active strategy for determining if a specified feature should be enabled or not.
func WithFallback ¶
func WithFallback(fallback bool) FeatureOption
WithFallback specifies what the value should be if the feature toggle is not found on the unleash service.
func WithFallbackFunc ¶
func WithFallbackFunc(fallback FallbackFunc) FeatureOption
WithFallbackFunc specifies a fallback function to evaluate a feature toggle in the event that it is not found on the service.
type MetricListener ¶
type MetricListener interface { // OnCount is called whenever the specified feature is queried. OnCount(string, bool) // OnSent is called whenever the server has successfully sent metrics to the server. OnSent(MetricsData) // OnRegistered is called whenever the client has successfully registered with the metrics server. OnRegistered(ClientData) }
MetricListener defines an interface that can be implemented in order to receive events that are relevant to sending metrics.
type MetricsData ¶
type MetricsData struct { // AppName is the name of the application. AppName string `json:"appName"` // InstanceID is the instance identifier. InstanceID string `json:"instanceId"` // Bucket is the payload data sent to the server. Bucket api.Bucket `json:"bucket"` }
MetricsData represents the data sent to the unleash server.
type RepositoryListener ¶
type RepositoryListener interface { // OnReady is called when the client has loaded the feature toggles from // the Unleash server. OnReady() }
RepositoryListener defines an interface that can be implemented in order to receive events that are relevant to the feature toggle repository.
type Storage ¶
type Storage interface { // Init is called to initialize the storage implementation. The backupPath // is used to specify the location the data should be stored and the appName // can be used in naming. Init(backupPath string, appName string) // Reset is called after the repository has fetched the feature toggles from the server. // If persist is true the implementation of this function should call Persist(). The data // passed in here should be owned by the implementer of this interface. Reset(data map[string]interface{}, persist bool) error // Load is called to load the data from persistent storage and hold it in memory for fast // querying. Load() error // Persist is called when the data in the storage implementation should be persisted to disk. Persist() error // Get returns the data for the specified feature toggle. Get(string) (interface{}, bool) // List returns a list of all feature toggles. List() []interface{} }
Storage is an interface that can be implemented in order to have control over how the repository of feature toggles is persisted.