Skip to content

Commit ec66647

Browse files
committed
Add evaluation context support
1 parent 7b73f08 commit ec66647

File tree

8 files changed

+366
-79
lines changed

8 files changed

+366
-79
lines changed

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.EXPORT_ALL_VARIABLES:
2+
3+
EVALUATION_CONTEXT_SCHEMA_URL ?= https://raw.githubusercontent.com/Flagsmith/flagsmith/feat/evaluation-context-schema/sdk/evaluation-context.json
4+
5+
6+
.PHONY: generate-evaluation-context
7+
generate-evaluation-context:
8+
npx quicktype ${EVALUATION_CONTEXT_SCHEMA_URL} --src-lang schema --lang go --package flagsmith --omit-empty --just-types-and-package > evaluationcontext.go

client.go

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@ import (
1414
"github.com/Flagsmith/flagsmith-go-client/v3/flagengine/segments"
1515
"github.com/Flagsmith/flagsmith-go-client/v3/internal/flaghttp"
1616

17-
. "github.com/Flagsmith/flagsmith-go-client/v3/flagengine/identities/traits"
17+
enginetraits "github.com/Flagsmith/flagsmith-go-client/v3/flagengine/identities/traits"
1818
)
1919

20+
type contextKey string
21+
22+
var contextKeyEvaluationContext = contextKey("evaluationContext")
23+
2024
// Client provides various methods to query Flagsmith API.
2125
type Client struct {
2226
apiKey string
@@ -44,8 +48,8 @@ func NewClient(apiKey string, options ...Option) *Client {
4448
}
4549

4650
c.client.SetHeaders(map[string]string{
47-
"Accept": "application/json",
48-
"X-Environment-Key": c.apiKey,
51+
"Accept": "application/json",
52+
flaghttp.EnvironmentKeyHeader: c.apiKey,
4953
})
5054
c.client.SetTimeout(c.config.timeout)
5155
c.log = createLogger()
@@ -87,9 +91,34 @@ func NewClient(apiKey string, options ...Option) *Client {
8791

8892
// Returns `Flags` struct holding all the flags for the current environment.
8993
//
94+
// Provide `EvaluationContext` to evaluate flags for a specific environment or identity.
95+
//
9096
// If local evaluation is enabled this function will not call the Flagsmith API
9197
// directly, but instead read the asynchronously updated local environment or
9298
// use the default flag handler in case it has not yet been updated.
99+
//
100+
// Notes:
101+
//
102+
// * `EvaluationContext.Environment` is ignored in local evaluation mode.
103+
//
104+
// * `EvaluationContext.Feature` is not yet supported.
105+
func (c *Client) GetFlags(ctx context.Context, ec *EvaluationContext) (f Flags, err error) {
106+
if ec != nil {
107+
ctx = context.WithValue(ctx, contextKeyEvaluationContext, ec)
108+
if ec.Identity != nil {
109+
return c.GetIdentityFlags(ctx, ec.Identity.Identifier, mapIdentityEvaluationContextToTraits(*ec.Identity))
110+
}
111+
}
112+
return c.GetEnvironmentFlags(ctx)
113+
}
114+
115+
// Returns `Flags` struct holding all the flags for the current environment.
116+
//
117+
// If local evaluation is enabled this function will not call the Flagsmith API
118+
// directly, but instead read the asynchronously updated local environment or
119+
// use the default flag handler in case it has not yet been updated.
120+
//
121+
// Deprecated: Use `GetFlags` instead.
93122
func (c *Client) GetEnvironmentFlags(ctx context.Context) (f Flags, err error) {
94123
if c.config.localEvaluation || c.config.offlineMode {
95124
if f, err = c.getEnvironmentFlagsFromEnvironment(); err == nil {
@@ -108,10 +137,6 @@ func (c *Client) GetEnvironmentFlags(ctx context.Context) (f Flags, err error) {
108137
return Flags{}, &FlagsmithClientError{msg: fmt.Sprintf("Failed to fetch flags with error: %s", err)}
109138
}
110139

111-
type GetIdentityFlagsOpts struct {
112-
Transient bool `json:"transient,omitempty"`
113-
}
114-
115140
// Returns `Flags` struct holding all the flags for the current environment for
116141
// a given identity.
117142
//
@@ -122,13 +147,15 @@ type GetIdentityFlagsOpts struct {
122147
// If local evaluation is enabled this function will not call the Flagsmith API
123148
// directly, but instead read the asynchronously updated local environment or
124149
// use the default flag handler in case it has not yet been updated.
125-
func (c *Client) GetIdentityFlags(ctx context.Context, identifier string, traits []*Trait, opts *GetIdentityFlagsOpts) (f Flags, err error) {
150+
//
151+
// Deprecated: Use `GetFlags` providing `EvaluationContext.Identity` instead.
152+
func (c *Client) GetIdentityFlags(ctx context.Context, identifier string, traits []*Trait) (f Flags, err error) {
126153
if c.config.localEvaluation || c.config.offlineMode {
127154
if f, err = c.getIdentityFlagsFromEnvironment(identifier, traits); err == nil {
128155
return f, nil
129156
}
130157
} else {
131-
if f, err = c.GetIdentityFlagsFromAPI(ctx, identifier, traits, opts); err == nil {
158+
if f, err = c.GetIdentityFlagsFromAPI(ctx, identifier, traits); err == nil {
132159
return f, nil
133160
}
134161
}
@@ -180,7 +207,15 @@ func (c *Client) BulkIdentify(ctx context.Context, batch []*IdentityTraits) erro
180207
// GetEnvironmentFlagsFromAPI tries to contact the Flagsmith API to get the latest environment data.
181208
// Will return an error in case of failure or unexpected response.
182209
func (c *Client) GetEnvironmentFlagsFromAPI(ctx context.Context) (Flags, error) {
183-
resp, err := c.client.NewRequest().
210+
req := c.client.NewRequest()
211+
maybeEc := ctx.Value(contextKeyEvaluationContext)
212+
if maybeEc != nil {
213+
envCtx := maybeEc.(*EvaluationContext).Environment
214+
if envCtx != nil {
215+
req.SetHeader(flaghttp.EnvironmentKeyHeader, envCtx.APIKey)
216+
}
217+
}
218+
resp, err := req.
184219
SetContext(ctx).
185220
ForceContentType("application/json").
186221
Get(c.config.baseURL + "flags/")
@@ -195,16 +230,27 @@ func (c *Client) GetEnvironmentFlagsFromAPI(ctx context.Context) (Flags, error)
195230

196231
// GetIdentityFlagsFromAPI tries to contact the Flagsmith API to get the latest identity flags.
197232
// Will return an error in case of failure or unexpected response.
198-
func (c *Client) GetIdentityFlagsFromAPI(ctx context.Context, identifier string, traits []*Trait, opts *GetIdentityFlagsOpts) (Flags, error) {
233+
func (c *Client) GetIdentityFlagsFromAPI(ctx context.Context, identifier string, traits []*Trait) (Flags, error) {
199234
body := struct {
200235
Identifier string `json:"identifier"`
201236
Traits []*Trait `json:"traits,omitempty"`
202-
GetIdentityFlagsOpts
237+
Transient *bool `json:"transient,omitempty"`
203238
}{Identifier: identifier, Traits: traits}
204-
if opts != nil {
205-
body.Transient = opts.Transient
239+
req := c.client.NewRequest()
240+
maybeEc := ctx.Value(contextKeyEvaluationContext)
241+
if maybeEc != nil {
242+
ec := maybeEc.(*EvaluationContext)
243+
envCtx := ec.Environment
244+
if envCtx != nil {
245+
req.SetHeader(flaghttp.EnvironmentKeyHeader, envCtx.APIKey)
246+
}
247+
idCtx := ec.Identity
248+
if idCtx != nil {
249+
// `Identifier` and `Traits` had been set by `GetFlags` earlier.
250+
body.Transient = &idCtx.Transient
251+
}
206252
}
207-
resp, err := c.client.NewRequest().
253+
resp, err := req.
208254
SetBody(&body).
209255
SetContext(ctx).
210256
ForceContentType("application/json").
@@ -295,7 +341,7 @@ func (c *Client) UpdateEnvironment(ctx context.Context) error {
295341
}
296342

297343
func (c *Client) getIdentityModel(identifier string, apiKey string, traits []*Trait) identities.IdentityModel {
298-
identityTraits := make([]*TraitModel, len(traits))
344+
identityTraits := make([]*enginetraits.TraitModel, len(traits))
299345
for i, trait := range traits {
300346
identityTraits[i] = trait.ToTraitModel()
301347
}

0 commit comments

Comments
 (0)