Skip to content

Commit a8501c0

Browse files
authored
feat!: engine evaluation context (#178)
* update engine test data * fetch flags using eval ctx for env flags * use context for identity flags * store and use engine eval context instead of env doc * use eval context for getidentity segments * Refactor: Move evaluator to engine_eval * rename * remove makefile trigger * misc * squash! * use the new schema * Bump major version * Add missing operators * remove old interface * update engine test data * remove go get * use go 1.24 * refactor: create a function for processing segments * refac: create a diff function for processing features * refac: squash * Don't use pointer for feature context * refac default priority * refac splitoperator * use stringArry for in operator * more refac and add reason to mv * misc * use any * use generic for segment operator * Short-Circuit conditions * review change * use new integration tests * fix percentage split operator * rename some functions * cleanup/refac * feat: Add support for .jsonc test files with comments using hujson - Added github.com/tailscale/hujson dependency for parsing JSON with comments - Updated integration tests to discover and process both .json and .jsonc files - JSONC files are automatically standardized to JSON before parsing - Improved test file name extraction to handle multiple extensions * fix: Configure misspell linter * make matching condition more dry * bump engine test data * move trait to it's own package to avoid circular import * clean mappers * use segment metadata to filter identity segments * use v2.1.0 tag * get rid of ValueUnion * compare segmes * fix: handle JSONPath fallback for non-primitive values and invalid paths - Update engine-test-data to v2.3.0 for new test cases - Add isPrimitive() helper to detect non-primitive JSONPath results - Fall back to trait lookup when JSONPath returns objects/arrays - Fall back to trait lookup when JSONPath parsing fails - Fixes edge cases with trait keys that look like JSONPath (e.g., '$.identity') * feat: add priority sorting for multivariate feature variants - Add Priority field to FeatureValue struct - Sort variants by priority before weight-based selection - Lower priority value = higher priority when variants overlap - Fixes multivariate feature flag variant priority sorting * Use feature value id as priority? * compare segment metadata * Add env name * use feat/variant-priority-sorting * fix non-deterministic map iteration order * use big int for priority * fix mapper * use engine-test-data v2.4.0 * implement feature metadata and bump engine-test-data
1 parent 7e2ea37 commit a8501c0

37 files changed

+3553
-1173
lines changed

.github/workflows/go.yml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ on:
77

88
jobs:
99
build:
10-
if: github.event.pull_request.draft == false
1110
name: Build
1211
runs-on: ubuntu-latest
1312

@@ -29,11 +28,7 @@ jobs:
2928
uses: actions/checkout@v4
3029
with:
3130
submodules: recursive
32-
33-
- name: Get dependencies
34-
run: |
35-
go get -v -t -d ./...
36-
31+
3732
- name: Lint
3833
uses: golangci/golangci-lint-action@v6
3934

.golangci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
linters-settings:
22
misspell:
33
locale: UK
4+
ignore-words:
5+
- standardize # hujson library uses US spelling
46
linters:
57
enable:
68
- contextcheck

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[![Go](https://github.com/flagsmith/flagsmith-go-client/workflows/Go/badge.svg?branch=main)](https://github.com/flagsmith/flagsmith-go-client/actions)
22
[![GoReportCard](https://goreportcard.com/badge/github.com/flagsmith/flagsmith-go-client)](https://goreportcard.com/report/github.com/flagsmith/flagsmith-go-client)
3-
[![GoDoc](https://godoc.org/github.com/flagsmith/flagsmith-go-client/v4?status.svg)](https://pkg.go.dev/github.com/Flagsmith/flagsmith-go-client/v4#section-documentation)
3+
[![GoDoc](https://godoc.org/github.com/flagsmith/flagsmith-go-client/v5?status.svg)](https://pkg.go.dev/github.com/Flagsmith/flagsmith-go-client/v5#section-documentation)
44

55
# Flagsmith Go SDK
66

client.go

Lines changed: 23 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@ import (
1111
"sync/atomic"
1212
"time"
1313

14-
"github.com/Flagsmith/flagsmith-go-client/v4/flagengine"
15-
"github.com/Flagsmith/flagsmith-go-client/v4/flagengine/environments"
16-
"github.com/Flagsmith/flagsmith-go-client/v4/flagengine/identities"
17-
"github.com/Flagsmith/flagsmith-go-client/v4/flagengine/segments"
14+
"github.com/Flagsmith/flagsmith-go-client/v5/flagengine"
15+
"github.com/Flagsmith/flagsmith-go-client/v5/flagengine/engine_eval"
16+
"github.com/Flagsmith/flagsmith-go-client/v5/flagengine/environments"
17+
"github.com/Flagsmith/flagsmith-go-client/v5/flagengine/segments"
1818
"github.com/go-resty/resty/v2"
19-
20-
enginetraits "github.com/Flagsmith/flagsmith-go-client/v4/flagengine/identities/traits"
2119
)
2220

2321
type contextKey string
@@ -30,7 +28,7 @@ type Client struct {
3028
config config
3129

3230
environment atomic.Value
33-
identitiesWithOverrides atomic.Value
31+
engineEvaluationContext atomic.Value
3432

3533
analyticsProcessor *AnalyticsProcessor
3634
realtime *realtime
@@ -141,7 +139,10 @@ func NewClient(apiKey string, options ...Option) *Client {
141139
panic("local evaluation and offline handler cannot be used together.")
142140
}
143141
if c.offlineHandler != nil {
144-
c.environment.Store(c.offlineHandler.GetEnvironment())
142+
env := c.offlineHandler.GetEnvironment()
143+
c.environment.Store(env)
144+
engineEvalCtx := engine_eval.MapEnvironmentDocumentToEvaluationContext(env)
145+
c.engineEvaluationContext.Store(&engineEvalCtx)
145146
}
146147

147148
if c.config.localEvaluation {
@@ -230,9 +231,10 @@ func (c *Client) GetIdentityFlags(ctx context.Context, identifier string, traits
230231

231232
// Returns an array of segments that the given identity is part of.
232233
func (c *Client) GetIdentitySegments(identifier string, traits []*Trait) ([]*segments.SegmentModel, error) {
233-
if env, ok := c.environment.Load().(*environments.EnvironmentModel); ok {
234-
identity := c.getIdentityModel(identifier, env.APIKey, traits)
235-
return flagengine.GetIdentitySegments(env, &identity), nil
234+
if evalCtx, ok := c.engineEvaluationContext.Load().(*engine_eval.EngineEvaluationContext); ok {
235+
engineEvalCtx := engine_eval.MapContextAndIdentityDataToContext(*evalCtx, identifier, traits)
236+
result := flagengine.GetEvaluationResult(&engineEvalCtx)
237+
return engine_eval.MapEvaluationResultSegmentsToSegmentModels(&result), nil
236238
}
237239
return nil, &FlagsmithClientError{msg: "flagsmith: Local evaluation required to obtain identity segments"}
238240
}
@@ -333,32 +335,22 @@ func (c *Client) GetIdentityFlagsFromAPI(ctx context.Context, identifier string,
333335
}
334336

335337
func (c *Client) getIdentityFlagsFromEnvironment(identifier string, traits []*Trait) (Flags, error) {
336-
env, ok := c.environment.Load().(*environments.EnvironmentModel)
338+
evalCtx, ok := c.engineEvaluationContext.Load().(*engine_eval.EngineEvaluationContext)
337339
if !ok {
338340
return Flags{}, fmt.Errorf("flagsmith: local environment has not yet been updated")
339341
}
340-
identity := c.getIdentityModel(identifier, env.APIKey, traits)
341-
featureStates := flagengine.GetIdentityFeatureStates(env, &identity)
342-
flags := makeFlagsFromFeatureStates(
343-
featureStates,
344-
c.analyticsProcessor,
345-
c.defaultFlagHandler,
346-
identifier,
347-
)
348-
return flags, nil
342+
engineEvalCtx := engine_eval.MapContextAndIdentityDataToContext(*evalCtx, identifier, traits)
343+
result := flagengine.GetEvaluationResult(&engineEvalCtx)
344+
return makeFlagsFromEngineEvaluationResult(&result, c.analyticsProcessor, c.defaultFlagHandler), nil
349345
}
350346

351347
func (c *Client) getEnvironmentFlagsFromEnvironment() (Flags, error) {
352-
env, ok := c.environment.Load().(*environments.EnvironmentModel)
348+
evalCtx, ok := c.engineEvaluationContext.Load().(*engine_eval.EngineEvaluationContext)
353349
if !ok {
354350
return Flags{}, fmt.Errorf("flagsmith: local environment has not yet been updated")
355351
}
356-
return makeFlagsFromFeatureStates(
357-
env.FeatureStates,
358-
c.analyticsProcessor,
359-
c.defaultFlagHandler,
360-
"",
361-
), nil
352+
result := flagengine.GetEvaluationResult(evalCtx)
353+
return makeFlagsFromEngineEvaluationResult(&result, c.analyticsProcessor, c.defaultFlagHandler), nil
362354
}
363355

364356
func (c *Client) pollEnvironment(ctx context.Context, pollForever bool) {
@@ -461,35 +453,13 @@ func (c *Client) UpdateEnvironment(ctx context.Context) error {
461453
isNew = true
462454
}
463455
c.environment.Store(&env)
464-
identitiesWithOverrides := make(map[string]identities.IdentityModel)
465-
for _, id := range env.IdentityOverrides {
466-
identitiesWithOverrides[id.Identifier] = *id
467-
}
468-
c.identitiesWithOverrides.Store(identitiesWithOverrides)
456+
457+
engineEvalCtx := engine_eval.MapEnvironmentDocumentToEvaluationContext(&env)
458+
c.engineEvaluationContext.Store(&engineEvalCtx)
469459

470460
if isNew {
471461
c.log.Info("environment updated", "environment", env.APIKey, "updated_at", env.UpdatedAt)
472462
}
473463

474464
return nil
475465
}
476-
477-
func (c *Client) getIdentityModel(identifier string, apiKey string, traits []*Trait) identities.IdentityModel {
478-
identityTraits := make([]*enginetraits.TraitModel, len(traits))
479-
for i, trait := range traits {
480-
identityTraits[i] = trait.ToTraitModel()
481-
}
482-
483-
identitiesWithOverrides, _ := c.identitiesWithOverrides.Load().(map[string]identities.IdentityModel)
484-
identity, ok := identitiesWithOverrides[identifier]
485-
if ok {
486-
identity.IdentityTraits = identityTraits
487-
return identity
488-
}
489-
490-
return identities.IdentityModel{
491-
Identifier: identifier,
492-
IdentityTraits: identityTraits,
493-
EnvironmentAPIKey: apiKey,
494-
}
495-
}

client_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import (
1313
"testing"
1414
"time"
1515

16-
flagsmith "github.com/Flagsmith/flagsmith-go-client/v4"
17-
"github.com/Flagsmith/flagsmith-go-client/v4/fixtures"
16+
flagsmith "github.com/Flagsmith/flagsmith-go-client/v5"
17+
"github.com/Flagsmith/flagsmith-go-client/v5/fixtures"
1818
"github.com/go-resty/resty/v2"
1919
"github.com/stretchr/testify/assert"
2020
)

flagengine/engine-test-data

Submodule engine-test-data updated 190 files

0 commit comments

Comments
 (0)