ldclient

package module
v2.0.0-...-4c2bfec Latest Latest
Warning

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

Go to latest
Published: Mar 5, 2018 License: Apache-2.0 Imports: 26 Imported by: 1

README

LaunchDarkly SDK for Go

Circle CI

Quick setup

  1. Install the SDK with the go tool:
go get gopkg.in/launchdarkly/go-client.v2
  1. Import the LaunchDarkly client:
import ld "gopkg.in/launchdarkly/go-client.v2"
  1. Create a new LDClient with your SDK key:
ldClient, err := ld.MakeClient("YOUR_SDK_KEY", 5*time.Second)
if err != nil {
    log.Fatalf("Error creating launch darkly client: %s", err)
}
defer ldClient.Close()

If you are reusing a global instance you probably want to not defer ldClient.Close() but instead close it when the application exits.

HTTPS proxy

Go's standard HTTP library provides built-in support for the use of a HTTPS proxy. If the HTTPS_PROXY environment variable is present then the SDK will proxy all network requests through the URL provided.

How to set the HTTPS_PROXY environment variable on Mac/Linux systems:

export HTTPS_PROXY=https://web-proxy.domain.com:8080

How to set the HTTPS_PROXY environment variable on Windows systems:

set HTTPS_PROXY=https://web-proxy.domain.com:8080

Your first feature flag

  1. Create a new feature flag on your dashboard
  2. In your application code, use the feature's key to check whether the flag is on for each user:
key := "user@test.com"
showFeature, _ := ldClient.BoolVariation("your.flag.key", ld.User{Key: &key}, false)
if showFeature {
    // application code to show the feature
} else {
    // the code to run if the feature is off
}

Building without Redis

The SDK includes an adapter for caching feature flag data in Redis, using Redigo. If you don't intend to use Redis and would like to avoid installing Redigo, add the following build tag to your go build command:

-tags launchdarkly_no_redis

Learn more

Check out our documentation for in-depth instructions on configuring and using LaunchDarkly. You can also head straight to the complete reference guide for this SDK.

Testing

We run integration tests for all our SDKs using a centralized test harness. This approach gives us the ability to test for consistency across SDKs, as well as test networking behavior in a long-running application. These tests cover each method in the SDK, and verify that event sending, flag evaluation, stream reconnection, and other aspects of the SDK all behave correctly.

Contributing

We encourage pull-requests and other contributions from the community. We've also published an SDK contributor's guide that provides a detailed explanation of how our SDKs work.

About LaunchDarkly

  • LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard. With LaunchDarkly, you can:
    • Roll out a new feature to a subset of your users (like a group of users who opt-in to a beta tester group), gathering feedback and bug reports from real-world use cases.
    • Gradually roll out a feature to an increasing percentage of users, and track the effect that the feature has on key metrics (for instance, how likely is a user to complete a purchase if they have feature A versus feature B?).
    • Turn off a feature that you realize is causing performance problems in production, without needing to re-deploy, or even restart the application with a changed configuration file.
    • Grant access to certain features based on user attributes, like payment plan (eg: users on the ‘gold’ plan get access to more features than users in the ‘silver’ plan). Disable parts of your application to facilitate maintenance, without taking everything offline.
  • LaunchDarkly provides feature flag SDKs for
  • Explore LaunchDarkly

Documentation

Index

Constants

View Source
const (
	FEATURE_REQUEST_EVENT = "feature"
	CUSTOM_EVENT          = "custom"
	IDENTIFY_EVENT        = "identify"
)
View Source
const (
	LatestFlagsPath = "/sdk/latest-flags"
)
View Source
const MinimumPollInterval = 30 * time.Second

The minimum value for Config.PollInterval. If you specify a smaller interval, the minimum will be used instead.

View Source
const Version = "2.3.0"

Variables

View Source
var DefaultConfig = Config{
	BaseUri:       "https://app.launchdarkly.com",
	StreamUri:     "https://stream.launchdarkly.com",
	EventsUri:     "https://events.launchdarkly.com",
	Capacity:      1000,
	FlushInterval: 5 * time.Second,
	PollInterval:  MinimumPollInterval,
	Logger:        log.New(os.Stderr, "[LaunchDarkly]", log.LstdFlags),
	Timeout:       3000 * time.Millisecond,
	Stream:        true,
	FeatureStore:  nil,
	UseLdd:        false,
	SendEvents:    true,
	Offline:       false,
}

Provides the default configuration options for the LaunchDarkly client. The easiest way to create a custom configuration is to start with the default config, and set the custom options from there. For example:

var config = DefaultConfig
config.Capacity = 2000
View Source
var ErrClientNotInitialized = errors.New("Feature flag evaluation called before LaunchDarkly client initialization completed")
View Source
var ErrInitializationTimeout = errors.New("Timeout encountered waiting for LaunchDarkly client initialization")

Functions

func ParseFloat64

func ParseFloat64(input interface{}) *float64

Parses numeric value as float64 from a string or another numeric type. Returns nil pointer if input is nil or unparsable.

func ParseTime

func ParseTime(input interface{}) *time.Time

Converts any of the following into a pointer to a time.Time value:

RFC3339/ISO8601 timestamp (example: 2016-04-16T17:09:12.759-07:00)
Unix epoch milliseconds as string
Unix milliseconds as number

Passing in a time.Time value will return a pointer to the input value. Unparsable inputs will return nil More info on RFC3339: http://stackoverflow.com/questions/522251/whats-the-difference-between-iso-8601-and-rfc-3339-date-formats

func ToJsonRawMessage

func ToJsonRawMessage(input interface{}) (json.RawMessage, error)

Converts input to a *json.RawMessage if possible.

Types

type BaseEvent

type BaseEvent struct {
	CreationDate uint64 `json:"creationDate"`
	Key          string `json:"key"`
	Kind         string `json:"kind"`
	User         User   `json:"user"`
}

type Clause

type Clause struct {
	Attribute string        `json:"attribute" bson:"attribute"`
	Op        Operator      `json:"op" bson:"op"`
	Values    []interface{} `json:"values" bson:"values"` // An array, interpreted as an OR of values
	Negate    bool          `json:"negate" bson:"negate"`
}

type Config

type Config struct {
	BaseUri               string
	StreamUri             string
	EventsUri             string
	Capacity              int
	FlushInterval         time.Duration
	SamplingInterval      int32
	PollInterval          time.Duration
	Logger                *log.Logger
	Timeout               time.Duration
	Stream                bool
	FeatureStore          FeatureStore
	UseLdd                bool
	SendEvents            bool
	Offline               bool
	AllAttributesPrivate  bool
	PrivateAttributeNames []string
}

Exposes advanced configuration options for the LaunchDarkly client.

type CustomEvent

type CustomEvent struct {
	BaseEvent
	Data interface{} `json:"data"`
}

func NewCustomEvent

func NewCustomEvent(key string, user User, data interface{}) CustomEvent

Constructs a new custom event, but does not send it. Typically, Track should be used to both create the event and send it to LaunchDarkly.

func (CustomEvent) GetBase

func (evt CustomEvent) GetBase() BaseEvent

func (CustomEvent) GetKind

func (evt CustomEvent) GetKind() string

type DerivedAttribute

type DerivedAttribute struct {
	Value       interface{} `json:"value" bson:"value"`
	LastDerived time.Time   `json:"lastDerived" bson:"lastDerived"`
}

The Derived attribute map is for internal use by LaunchDarkly only. Derived attributes sent to LaunchDarkly are ignored.

type EvalResult

type EvalResult struct {
	Value                     interface{}
	Explanation               *Explanation
	PrerequisiteRequestEvents []FeatureRequestEvent //to be sent to LD
}

type Event

type Event interface {
	GetBase() BaseEvent
	GetKind() string
}

type Explanation

type Explanation struct {
	Kind                string `json:"kind" bson:"kind"`
	*Target             `json:"target,omitempty"`
	*Rule               `json:"rule,omitempty"`
	*Prerequisite       `json:"prerequisite,omitempty"`
	*VariationOrRollout `json:"fallthrough,omitempty"`
}

An explanation is one of: target, rule, prerequisite that wasn't met, or fallthrough rollout/variation

type Feature

type Feature struct {
	Name         *string      `json:"name"`
	Key          *string      `json:"key"`
	Kind         *string      `json:"kind"`
	Salt         *string      `json:"salt"`
	On           *bool        `json:"on"`
	Variations   *[]Variation `json:"variations"`
	CommitDate   *time.Time   `json:"commitDate"`
	CreationDate *time.Time   `json:"creationDate"`
	Version      int          `json:"version,omitempty"`
	Deleted      bool         `json:"deleted,omitempty"`
}

Legacy (v1) feature

func (Feature) Evaluate

func (f Feature) Evaluate(user User) (value interface{}, rulesPassed bool)

func (Feature) EvaluateExplain

func (f Feature) EvaluateExplain(user User) (value interface{}, targetMatch *TargetRule, rulesPassed bool)

type FeatureFlag

type FeatureFlag struct {
	Key           string             `json:"key" bson:"key"`
	Version       int                `json:"version" bson:"version"`
	On            bool               `json:"on" bson:"on"`
	Prerequisites []Prerequisite     `json:"prerequisites" bson:"prerequisites"`
	Salt          string             `json:"salt" bson:"salt"`
	Sel           string             `json:"sel" bson:"sel"`
	Targets       []Target           `json:"targets" bson:"targets"`
	Rules         []Rule             `json:"rules" bson:"rules"`
	Fallthrough   VariationOrRollout `json:"fallthrough" bson:"fallthrough"`
	OffVariation  *int               `json:"offVariation" bson:"offVariation"`
	Variations    []interface{}      `json:"variations" bson:"variations"`
	Deleted       bool               `json:"deleted" bson:"deleted"`
}

func (FeatureFlag) EvaluateExplain

func (f FeatureFlag) EvaluateExplain(user User, store FeatureStore) (*EvalResult, error)

type FeatureRequestEvent

type FeatureRequestEvent struct {
	BaseEvent
	Value    interface{} `json:"value"`
	Default  interface{} `json:"default"`
	Version  *int        `json:"version,omitempty"`
	PrereqOf *string     `json:"prereqOf,omitempty"`
}

func NewFeatureRequestEvent

func NewFeatureRequestEvent(key string, user User, value, defaultVal interface{}, version *int, prereqOf *string) FeatureRequestEvent

Used to just create the event. Normally, you don't need to call this; the event is created and queued automatically during feature flag evaluation.

func (FeatureRequestEvent) GetBase

func (evt FeatureRequestEvent) GetBase() BaseEvent

func (FeatureRequestEvent) GetKind

func (evt FeatureRequestEvent) GetKind() string

type FeatureStore

type FeatureStore interface {
	Get(key string) (*FeatureFlag, error)
	All() (map[string]*FeatureFlag, error)
	Init(map[string]*FeatureFlag) error
	Delete(key string, version int) error
	Upsert(key string, f FeatureFlag) error
	Initialized() bool
}

A data structure that maintains the live collection of features. It is used by LaunchDarkly when streaming mode is enabled, and stores feature events returned by the streaming API. Custom FeatureStore implementations can be passed to the LaunchDarkly client via a custom Config object. LaunchDarkly provides two FeatureStore implementations: one backed by an in-memory map, and one backed by Redis. Implementations must be thread-safe.

type HttpStatusError

type HttpStatusError struct {
	Message string
	Code    int
}

func (HttpStatusError) Error

func (e HttpStatusError) Error() string

type IdentifyEvent

type IdentifyEvent struct {
	BaseEvent
}

func NewIdentifyEvent

func NewIdentifyEvent(user User) IdentifyEvent

Constructs a new identify event, but does not send it. Typically, Identify should be used to both create the event and send it to LaunchDarkly.

func (IdentifyEvent) GetBase

func (evt IdentifyEvent) GetBase() BaseEvent

func (IdentifyEvent) GetKind

func (evt IdentifyEvent) GetKind() string

type InMemoryFeatureStore

type InMemoryFeatureStore struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

A memory based FeatureStore implementation, backed by a lock-striped map.

func NewInMemoryFeatureStore

func NewInMemoryFeatureStore(logger *log.Logger) *InMemoryFeatureStore

Creates a new in-memory FeatureStore instance.

func (*InMemoryFeatureStore) All

func (store *InMemoryFeatureStore) All() (map[string]*FeatureFlag, error)

func (*InMemoryFeatureStore) Delete

func (store *InMemoryFeatureStore) Delete(key string, version int) error

func (*InMemoryFeatureStore) Get

func (store *InMemoryFeatureStore) Get(key string) (*FeatureFlag, error)

func (*InMemoryFeatureStore) Init

func (store *InMemoryFeatureStore) Init(fs map[string]*FeatureFlag) error

func (*InMemoryFeatureStore) Initialized

func (store *InMemoryFeatureStore) Initialized() bool

func (*InMemoryFeatureStore) Upsert

func (store *InMemoryFeatureStore) Upsert(key string, f FeatureFlag) error

type LDClient

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

The LaunchDarkly client. Client instances are thread-safe. Applications should instantiate a single instance for the lifetime of their application.

func MakeClient

func MakeClient(sdkKey string, waitFor time.Duration) (*LDClient, error)

Creates a new client instance that connects to LaunchDarkly with the default configuration. In most cases, you should use this method to instantiate your client. The optional duration parameter allows callers to block until the client has connected to LaunchDarkly and is properly initialized.

func MakeCustomClient

func MakeCustomClient(sdkKey string, config Config, waitFor time.Duration) (*LDClient, error)

Creates a new client instance that connects to LaunchDarkly with a custom configuration. The optional duration parameter allows callers to block until the client has connected to LaunchDarkly and is properly initialized.

func (*LDClient) AllFlags

func (client *LDClient) AllFlags(user User) map[string]interface{}

Returns a map from feature flag keys to values for a given user. If the result of the flag's evaluation would result in the default value, `nil` will be returned. This method does not send analytics events back to LaunchDarkly

func (*LDClient) BoolVariation

func (client *LDClient) BoolVariation(key string, user User, defaultVal bool) (bool, error)

Returns the value of a boolean feature flag for a given user. Returns defaultVal if there is an error, if the flag doesn't exist, the client hasn't completed initialization, or the feature is turned off.

func (*LDClient) Close

func (client *LDClient) Close()

Shuts down the LaunchDarkly client. After calling this, the LaunchDarkly client should no longer be used.

func (*LDClient) Evaluate

func (client *LDClient) Evaluate(key string, user User, defaultVal interface{}) (interface{}, *int, error)

func (*LDClient) Float64Variation

func (client *LDClient) Float64Variation(key string, user User, defaultVal float64) (float64, error)

Returns the value of a feature flag (whose variations are floats) for the given user. Returns defaultVal if there is an error, if the flag doesn't exist, or the feature is turned off.

func (*LDClient) Flush

func (client *LDClient) Flush()

Immediately flushes queued events.

func (*LDClient) Identify

func (client *LDClient) Identify(user User) error

func (*LDClient) Initialized

func (client *LDClient) Initialized() bool

Returns whether the LaunchDarkly client is initialized.

func (*LDClient) IntVariation

func (client *LDClient) IntVariation(key string, user User, defaultVal int) (int, error)

Returns the value of a feature flag (whose variations are integers) for the given user. Returns defaultVal if there is an error, if the flag doesn't exist, or the feature is turned off.

func (*LDClient) IsOffline

func (client *LDClient) IsOffline() bool

Returns whether the LaunchDarkly client is in offline mode.

func (*LDClient) JsonVariation

func (client *LDClient) JsonVariation(key string, user User, defaultVal json.RawMessage) (json.RawMessage, error)

Returns the value of a feature flag (whose variations are JSON) for the given user. Returns defaultVal if there is an error, if the flag doesn't exist, or the feature is turned off.

func (*LDClient) SecureModeHash

func (client *LDClient) SecureModeHash(user User) string

func (*LDClient) StringVariation

func (client *LDClient) StringVariation(key string, user User, defaultVal string) (string, error)

Returns the value of a feature flag (whose variations are strings) for the given user. Returns defaultVal if there is an error, if the flag doesn't exist, or the feature is turned off.

func (*LDClient) Toggle deprecated

func (client *LDClient) Toggle(key string, user User, defaultVal bool) (bool, error)

Deprecated: Use BoolVariation().

func (*LDClient) Track

func (client *LDClient) Track(key string, user User, data interface{}) error

Tracks that a user has performed an event. Custom data can be attached to the event, and is serialized to JSON using the encoding/json package (http://golang.org/pkg/encoding/json/).

type Operator

type Operator string
const (
	OperatorIn                 Operator = "in"
	OperatorEndsWith           Operator = "endsWith"
	OperatorStartsWith         Operator = "startsWith"
	OperatorMatches            Operator = "matches"
	OperatorContains           Operator = "contains"
	OperatorLessThan           Operator = "lessThan"
	OperatorLessThanOrEqual    Operator = "lessThanOrEqual"
	OperatorGreaterThan        Operator = "greaterThan"
	OperatorGreaterThanOrEqual Operator = "greaterThanOrEqual"
	OperatorBefore             Operator = "before"
	OperatorAfter              Operator = "after"
	OperatorSemVerEqual        Operator = "semVerEqual"
	OperatorSemVerLessThan     Operator = "semVerLessThan"
	OperatorSemVerGreaterThan  Operator = "semVerGreaterThan"
)

func (Operator) Name

func (op Operator) Name() string

type Prerequisite

type Prerequisite struct {
	Key       string `json:"key"`
	Variation int    `json:"variation"`
}

type RedisFeatureStore

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

A Redis-backed feature store.

func NewRedisFeatureStore

func NewRedisFeatureStore(host string, port int, prefix string, timeout time.Duration, logger *log.Logger) *RedisFeatureStore

Constructs a new Redis-backed feature store connecting to the specified host and port with a default connection pool configuration (16 concurrent connections, connection requests block). Attaches a prefix string to all keys to namespace LaunchDarkly-specific keys. If the specified prefix is the empty string, it defaults to "launchdarkly"

func NewRedisFeatureStoreFromUrl

func NewRedisFeatureStoreFromUrl(url, prefix string, timeout time.Duration, logger *log.Logger) *RedisFeatureStore

Constructs a new Redis-backed feature store connecting to the specified URL with a default connection pool configuration (16 concurrent connections, connection requests block). Attaches a prefix string to all keys to namespace LaunchDarkly-specific keys. If the specified prefix is the empty string, it defaults to "launchdarkly".

func NewRedisFeatureStoreWithPool

func NewRedisFeatureStoreWithPool(pool *r.Pool, prefix string, timeout time.Duration, logger *log.Logger) *RedisFeatureStore

Constructs a new Redis-backed feature store with the specified redigo pool configuration. Attaches a prefix string to all keys to namespace LaunchDarkly-specific keys. If the specified prefix is the empty string, it defaults to "launchdarkly".

func (*RedisFeatureStore) All

func (store *RedisFeatureStore) All() (map[string]*FeatureFlag, error)

func (*RedisFeatureStore) Delete

func (store *RedisFeatureStore) Delete(key string, version int) error

func (*RedisFeatureStore) Get

func (store *RedisFeatureStore) Get(key string) (*FeatureFlag, error)

func (*RedisFeatureStore) Init

func (store *RedisFeatureStore) Init(features map[string]*FeatureFlag) error

func (*RedisFeatureStore) Initialized

func (store *RedisFeatureStore) Initialized() bool

func (*RedisFeatureStore) Upsert

func (store *RedisFeatureStore) Upsert(key string, f FeatureFlag) error

type Rollout

type Rollout struct {
	Variations []WeightedVariation `json:"variations" bson:"variations"`
	BucketBy   *string             `json:"bucketBy,omitempty" bson:"bucketBy,omitempty"`
}

type Rule

type Rule struct {
	VariationOrRollout `bson:",inline"`
	Clauses            []Clause `json:"clauses" bson:"clauses"`
}

Expresses a set of AND-ed matching conditions for a user, along with either a fixed variation or a set of rollout percentages

type Target

type Target struct {
	Values    []string `json:"values" bson:"values"`
	Variation int      `json:"variation" bson:"variation"`
}

type TargetRule

type TargetRule struct {
	Attribute string        `json:"attribute"`
	Op        Operator      `json:"op"`
	Values    []interface{} `json:"values"`
}

type User

type User struct {
	Key       *string                      `json:"key,omitempty" bson:"key,omitempty"`
	Secondary *string                      `json:"secondary,omitempty" bson:"secondary,omitempty"`
	Ip        *string                      `json:"ip,omitempty" bson:"ip,omitempty"`
	Country   *string                      `json:"country,omitempty" bson:"country,omitempty"`
	Email     *string                      `json:"email,omitempty" bson:"email,omitempty"`
	FirstName *string                      `json:"firstName,omitempty" bson:"firstName,omitempty"`
	LastName  *string                      `json:"lastName,omitempty" bson:"lastName,omitempty"`
	Avatar    *string                      `json:"avatar,omitempty" bson:"avatar,omitempty"`
	Name      *string                      `json:"name,omitempty" bson:"name,omitempty"`
	Anonymous *bool                        `json:"anonymous,omitempty" bson:"anonymous,omitempty"`
	Custom    *map[string]interface{}      `json:"custom,omitempty" bson:"custom,omitempty"`
	Derived   map[string]*DerivedAttribute `json:"derived,omitempty" bson:"derived,omitempty"`

	// PrivateAttributes contains a list of attribute names that were included in the user,
	// but were marked as private. As such, these attributes are not included in the fields above.
	PrivateAttributes []string `json:"privateAttrs,omitempty" bson:"privateAttrs,omitempty"`

	// This contains list of attributes to keep private, whether they appear at the top-level or Custom
	// The attribute "key" is always sent regardless of whether it is in this list, and "custom" cannot be used to
	// eliminate all custom attributes
	PrivateAttributeNames []string `json:"-" bson:"-"`
}

A User contains specific attributes of a user browsing your site. The only mandatory property property is the Key, which must uniquely identify each user. For authenticated users, this may be a username or e-mail address. For anonymous users, this could be an IP address or session ID.

Besides the mandatory Key, User supports two kinds of optional attributes: interpreted attributes (e.g. Ip and Country) and custom attributes. LaunchDarkly can parse interpreted attributes and attach meaning to them. For example, from an Ip address, LaunchDarkly can do a geo IP lookup and determine the user's country.

Custom attributes are not parsed by LaunchDarkly. They can be used in custom rules-- for example, a custom attribute such as "customer_ranking" can be used to launch a feature to the top 10% of users on a site.

func NewAnonymousUser

func NewAnonymousUser(key string) User

NewAnonymousUser creates a new anonymous user identified by the given key.

func NewUser

func NewUser(key string) User

NewUser creates a new user identified by the given key.

type Variation

type Variation struct {
	Value      interface{}  `json:"value"`
	Weight     int          `json:"weight"`
	Targets    []TargetRule `json:"targets"`
	UserTarget *TargetRule  `json:"userTarget,omitempty"`
}

type VariationOrRollout

type VariationOrRollout struct {
	Variation *int     `json:"variation,omitempty" bson:"variation,omitempty"`
	Rollout   *Rollout `json:"rollout,omitempty" bson:"rollout,omitempty"`
}

Contains either the fixed variation or percent rollout to serve. Invariant: one of the variation or rollout must be non-nil.

type WeightedVariation

type WeightedVariation struct {
	Variation int `json:"variation" bson:"variation"`
	Weight    int `json:"weight" bson:"weight"` // Ranges from 0 to 100000
}

Jump to

Keyboard shortcuts

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