Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
95eb78f
wip: engine eval context
gagantrivedi Sep 30, 2025
50d47a8
update engine test data
gagantrivedi Sep 30, 2025
16b872e
fetch flags using eval ctx for env flags
gagantrivedi Sep 30, 2025
0c3e335
use context for identity flags
gagantrivedi Sep 30, 2025
984cb77
store and use engine eval context instead of env doc
gagantrivedi Sep 30, 2025
91a9ad1
use eval context for getidentity segments
gagantrivedi Sep 30, 2025
4fa190a
Refactor: Move evaluator to engine_eval
gagantrivedi Oct 1, 2025
b008591
rename
gagantrivedi Oct 1, 2025
fa8b3fd
remove makefile trigger
gagantrivedi Oct 1, 2025
ba2074e
misc
gagantrivedi Oct 1, 2025
49db0d2
squash!
gagantrivedi Oct 1, 2025
068c709
use the new schema
gagantrivedi Oct 1, 2025
e214b14
Bump major version
gagantrivedi Oct 1, 2025
4da51db
Add missing operators
gagantrivedi Oct 1, 2025
41105e6
remove old interface
gagantrivedi Oct 2, 2025
86dc538
update engine test data
gagantrivedi Oct 2, 2025
192883f
remove go get
gagantrivedi Oct 2, 2025
c778096
use go 1.24
gagantrivedi Oct 2, 2025
f85071a
refactor: create a function for processing segments
gagantrivedi Oct 2, 2025
a52a301
refac: create a diff function for processing features
gagantrivedi Oct 2, 2025
59d405c
refac: squash
gagantrivedi Oct 2, 2025
a91e5ec
Don't use pointer for feature context
gagantrivedi Oct 2, 2025
0790608
refac default priority
gagantrivedi Oct 2, 2025
5f5953d
refac splitoperator
gagantrivedi Oct 3, 2025
1899cf8
use stringArry for in operator
gagantrivedi Oct 3, 2025
379dbf8
more refac and add reason to mv
gagantrivedi Oct 3, 2025
71ca9d9
misc
gagantrivedi Oct 5, 2025
27d6208
use any
gagantrivedi Oct 6, 2025
23195ee
use generic for segment operator
gagantrivedi Oct 6, 2025
a9d0e51
Short-Circuit conditions
gagantrivedi Oct 6, 2025
9ca2100
review change
gagantrivedi Oct 6, 2025
f353451
use new integration tests
gagantrivedi Oct 7, 2025
a527b82
fix percentage split operator
gagantrivedi Oct 7, 2025
c983911
rename some functions
gagantrivedi Oct 7, 2025
d40556c
cleanup/refac
gagantrivedi Oct 7, 2025
02ec949
feat: Add support for .jsonc test files with comments using hujson
gagantrivedi Oct 7, 2025
5012e6e
fix: Configure misspell linter
gagantrivedi Oct 7, 2025
04d4951
make matching condition more dry
gagantrivedi Oct 8, 2025
8aa33b3
bump engine test data
gagantrivedi Oct 8, 2025
dec254a
move trait to it's own package to avoid circular import
gagantrivedi Oct 9, 2025
49f7fcd
clean mappers
gagantrivedi Oct 10, 2025
634ceda
use segment metadata to filter identity segments
gagantrivedi Oct 13, 2025
4a4cb00
use v2.1.0 tag
gagantrivedi Oct 13, 2025
edfee3e
get rid of ValueUnion
gagantrivedi Oct 13, 2025
19a1d2d
compare segmes
gagantrivedi Oct 13, 2025
e2ad91c
Merge branch 'main' into feat/context-again
gagantrivedi Oct 14, 2025
5356ba6
fix: handle JSONPath fallback for non-primitive values and invalid paths
gagantrivedi Oct 15, 2025
fc18e3b
feat: add priority sorting for multivariate feature variants
gagantrivedi Oct 15, 2025
6aeb5c6
Use feature value id as priority?
gagantrivedi Oct 15, 2025
dc8c317
compare segment metadata
gagantrivedi Oct 15, 2025
e9d2918
Add env name
gagantrivedi Oct 15, 2025
e05ab87
use feat/variant-priority-sorting
gagantrivedi Oct 15, 2025
89d2131
fix non-deterministic map iteration order
gagantrivedi Oct 15, 2025
7a6889e
use big int for priority
gagantrivedi Oct 15, 2025
361fc2c
fix mapper
gagantrivedi Oct 16, 2025
7476d43
use engine-test-data v2.4.0
gagantrivedi Oct 16, 2025
2ec8da2
implement feature metadata and bump engine-test-data
gagantrivedi Oct 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ on:

jobs:
build:
if: github.event.pull_request.draft == false
name: Build
runs-on: ubuntu-latest

Expand All @@ -29,11 +28,7 @@ jobs:
uses: actions/checkout@v4
with:
submodules: recursive

- name: Get dependencies
run: |
go get -v -t -d ./...


- name: Lint
uses: golangci/golangci-lint-action@v6

Expand Down
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
linters-settings:
misspell:
locale: UK
ignore-words:
- standardize # hujson library uses US spelling
linters:
enable:
- contextcheck
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![Go](https://github.com/flagsmith/flagsmith-go-client/workflows/Go/badge.svg?branch=main)](https://github.com/flagsmith/flagsmith-go-client/actions)
[![GoReportCard](https://goreportcard.com/badge/github.com/flagsmith/flagsmith-go-client)](https://goreportcard.com/report/github.com/flagsmith/flagsmith-go-client)
[![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)
[![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)

# Flagsmith Go SDK

Expand Down
76 changes: 23 additions & 53 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ import (
"sync/atomic"
"time"

"github.com/Flagsmith/flagsmith-go-client/v4/flagengine"
"github.com/Flagsmith/flagsmith-go-client/v4/flagengine/environments"
"github.com/Flagsmith/flagsmith-go-client/v4/flagengine/identities"
"github.com/Flagsmith/flagsmith-go-client/v4/flagengine/segments"
"github.com/Flagsmith/flagsmith-go-client/v5/flagengine"
"github.com/Flagsmith/flagsmith-go-client/v5/flagengine/engine_eval"
"github.com/Flagsmith/flagsmith-go-client/v5/flagengine/environments"
"github.com/Flagsmith/flagsmith-go-client/v5/flagengine/segments"
"github.com/go-resty/resty/v2"

enginetraits "github.com/Flagsmith/flagsmith-go-client/v4/flagengine/identities/traits"
)

type contextKey string
Expand All @@ -30,7 +28,7 @@ type Client struct {
config config

environment atomic.Value
identitiesWithOverrides atomic.Value
engineEvaluationContext atomic.Value

analyticsProcessor *AnalyticsProcessor
realtime *realtime
Expand Down Expand Up @@ -141,7 +139,10 @@ func NewClient(apiKey string, options ...Option) *Client {
panic("local evaluation and offline handler cannot be used together.")
}
if c.offlineHandler != nil {
c.environment.Store(c.offlineHandler.GetEnvironment())
env := c.offlineHandler.GetEnvironment()
c.environment.Store(env)
engineEvalCtx := engine_eval.MapEnvironmentDocumentToEvaluationContext(env)
c.engineEvaluationContext.Store(&engineEvalCtx)
}

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

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

func (c *Client) getIdentityFlagsFromEnvironment(identifier string, traits []*Trait) (Flags, error) {
env, ok := c.environment.Load().(*environments.EnvironmentModel)
evalCtx, ok := c.engineEvaluationContext.Load().(*engine_eval.EngineEvaluationContext)
if !ok {
return Flags{}, fmt.Errorf("flagsmith: local environment has not yet been updated")
}
identity := c.getIdentityModel(identifier, env.APIKey, traits)
featureStates := flagengine.GetIdentityFeatureStates(env, &identity)
flags := makeFlagsFromFeatureStates(
featureStates,
c.analyticsProcessor,
c.defaultFlagHandler,
identifier,
)
return flags, nil
engineEvalCtx := engine_eval.MapContextAndIdentityDataToContext(*evalCtx, identifier, traits)
result := flagengine.GetEvaluationResult(&engineEvalCtx)
return makeFlagsFromEngineEvaluationResult(&result, c.analyticsProcessor, c.defaultFlagHandler), nil
}

func (c *Client) getEnvironmentFlagsFromEnvironment() (Flags, error) {
env, ok := c.environment.Load().(*environments.EnvironmentModel)
evalCtx, ok := c.engineEvaluationContext.Load().(*engine_eval.EngineEvaluationContext)
if !ok {
return Flags{}, fmt.Errorf("flagsmith: local environment has not yet been updated")
}
return makeFlagsFromFeatureStates(
env.FeatureStates,
c.analyticsProcessor,
c.defaultFlagHandler,
"",
), nil
result := flagengine.GetEvaluationResult(evalCtx)
return makeFlagsFromEngineEvaluationResult(&result, c.analyticsProcessor, c.defaultFlagHandler), nil
}

func (c *Client) pollEnvironment(ctx context.Context, pollForever bool) {
Expand Down Expand Up @@ -461,35 +453,13 @@ func (c *Client) UpdateEnvironment(ctx context.Context) error {
isNew = true
}
c.environment.Store(&env)
identitiesWithOverrides := make(map[string]identities.IdentityModel)
for _, id := range env.IdentityOverrides {
identitiesWithOverrides[id.Identifier] = *id
}
c.identitiesWithOverrides.Store(identitiesWithOverrides)

engineEvalCtx := engine_eval.MapEnvironmentDocumentToEvaluationContext(&env)
c.engineEvaluationContext.Store(&engineEvalCtx)

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

return nil
}

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

identitiesWithOverrides, _ := c.identitiesWithOverrides.Load().(map[string]identities.IdentityModel)
identity, ok := identitiesWithOverrides[identifier]
if ok {
identity.IdentityTraits = identityTraits
return identity
}

return identities.IdentityModel{
Identifier: identifier,
IdentityTraits: identityTraits,
EnvironmentAPIKey: apiKey,
}
}
4 changes: 2 additions & 2 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
"testing"
"time"

flagsmith "github.com/Flagsmith/flagsmith-go-client/v4"
"github.com/Flagsmith/flagsmith-go-client/v4/fixtures"
flagsmith "github.com/Flagsmith/flagsmith-go-client/v5"
"github.com/Flagsmith/flagsmith-go-client/v5/fixtures"
"github.com/go-resty/resty/v2"
"github.com/stretchr/testify/assert"
)
Expand Down
2 changes: 1 addition & 1 deletion flagengine/engine-test-data
Submodule engine-test-data updated 190 files
Loading
Loading