Skip to content

Commit d0e37a1

Browse files
committed
more logs + pass EnvironmentContext structs and not pointers
1 parent 1966d3c commit d0e37a1

File tree

7 files changed

+55
-48
lines changed

7 files changed

+55
-48
lines changed

client.go

+15-14
Original file line numberDiff line numberDiff line change
@@ -173,20 +173,21 @@ func (c *Client) UpdateEnvironment(ctx context.Context) error {
173173
e := &FlagsmithAPIError{Msg: msg, Err: nil, ResponseStatusCode: resp.StatusCode(), ResponseStatus: resp.Status()}
174174
return c.handleError(e)
175175
}
176-
c.environment.SetEnvironment(&env)
176+
c.environment.SetEnvironment(env)
177177

178178
c.log.Info("environment updated", "environment", env.APIKey)
179179
return nil
180180
}
181181

182182
// GetIdentitySegments returns the segments that this evaluation context is a part of. It requires a local environment
183183
// provided by [WithLocalEvaluation] and/or [WithOfflineHandler].
184-
func (c *Client) GetIdentitySegments(ec EvaluationContext) ([]*segments.SegmentModel, error) {
185-
if env := c.environment.GetEnvironment(); env != nil {
186-
identity := c.getIdentityModel(ec.identifier, env.APIKey, ec.traits)
187-
return flagengine.GetIdentitySegments(env, &identity), nil
184+
func (c *Client) GetIdentitySegments(ec EvaluationContext) (s []*segments.SegmentModel, err error) {
185+
env, ok := c.environment.GetEnvironment()
186+
if !ok {
187+
return s, errors.New("GetIdentitySegments called with no local environment available")
188188
}
189-
return nil, &FlagsmithClientError{msg: "no local environment available to calculate identity segments"}
189+
identity := c.getIdentityModel(ec.identifier, env.APIKey, ec.traits)
190+
return flagengine.GetIdentitySegments(env, &identity), nil
190191
}
191192

192193
// BulkIdentify can be used to create/overwrite identities(with traits) in bulk
@@ -283,8 +284,8 @@ func (c *Client) getIdentityFlagsFromAPI(ctx context.Context, identifier string,
283284
}
284285

285286
func (c *Client) getEnvironmentFlagsFromEnvironment() (Flags, error) {
286-
env := c.environment.GetEnvironment()
287-
if env == nil {
287+
env, ok := c.environment.GetEnvironment()
288+
if !ok {
288289
return Flags{}, fmt.Errorf("getEnvironmentFlagsFromEnvironment: no local environment is available")
289290
}
290291
return makeFlagsFromFeatureStates(
@@ -296,8 +297,8 @@ func (c *Client) getEnvironmentFlagsFromEnvironment() (Flags, error) {
296297
}
297298

298299
func (c *Client) getIdentityFlagsFromEnvironment(identifier string, traits map[string]interface{}) (Flags, error) {
299-
env := c.environment.GetEnvironment()
300-
if env := c.environment.GetEnvironment(); env == nil {
300+
env, ok := c.environment.GetEnvironment()
301+
if !ok {
301302
return Flags{}, fmt.Errorf("getIdentityFlagsFromDocument: no local environment is available")
302303
}
303304
identity := c.getIdentityModel(identifier, env.APIKey, traits)
@@ -360,14 +361,14 @@ func (c *Client) handleError(err *FlagsmithAPIError) *FlagsmithAPIError {
360361

361362
type state struct {
362363
mu sync.RWMutex
363-
environment *environments.EnvironmentModel
364+
environment environments.EnvironmentModel
364365
identityOverrides sync.Map
365366
}
366367

367-
func (es *state) GetEnvironment() *environments.EnvironmentModel {
368+
func (es *state) GetEnvironment() (environments.EnvironmentModel, bool) {
368369
es.mu.RLock()
369370
defer es.mu.RUnlock()
370-
return es.environment
371+
return es.environment, es.environment.APIKey != ""
371372
}
372373

373374
func (es *state) GetIdentityOverride(identifier string) (identities.IdentityModel, bool) {
@@ -378,7 +379,7 @@ func (es *state) GetIdentityOverride(identifier string) (identities.IdentityMode
378379
return identities.IdentityModel{}, ok
379380
}
380381

381-
func (es *state) SetEnvironment(env *environments.EnvironmentModel) {
382+
func (es *state) SetEnvironment(env environments.EnvironmentModel) {
382383
es.mu.Lock()
383384
defer es.mu.Unlock()
384385
es.environment = env

flagengine/engine.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
)
1010

1111
// GetEnvironmentFeatureStates returns a list of feature states for a given environment.
12-
func GetEnvironmentFeatureStates(environment *environments.EnvironmentModel) []*features.FeatureStateModel {
12+
func GetEnvironmentFeatureStates(environment environments.EnvironmentModel) []*features.FeatureStateModel {
1313
if environment.Project.HideDisabledFlags {
1414
var featureStates []*features.FeatureStateModel
1515
for _, fs := range environment.FeatureStates {
@@ -23,7 +23,7 @@ func GetEnvironmentFeatureStates(environment *environments.EnvironmentModel) []*
2323
}
2424

2525
// GetEnvironmentFeatureState returns a specific feature state for a given featureName in a given environment, or nil feature state is not found.
26-
func GetEnvironmentFeatureState(environment *environments.EnvironmentModel, featureName string) *features.FeatureStateModel {
26+
func GetEnvironmentFeatureState(environment environments.EnvironmentModel, featureName string) *features.FeatureStateModel {
2727
for _, fs := range environment.FeatureStates {
2828
if fs.Feature.Name == featureName {
2929
return fs
@@ -34,7 +34,7 @@ func GetEnvironmentFeatureState(environment *environments.EnvironmentModel, feat
3434

3535
// GetIdentityFeatureStates returns a list of feature states for a given identity in a given environment.
3636
func GetIdentityFeatureStates(
37-
environment *environments.EnvironmentModel,
37+
environment environments.EnvironmentModel,
3838
identity *identities.IdentityModel,
3939
overrideTraits ...*traits.TraitModel,
4040
) []*features.FeatureStateModel {
@@ -52,7 +52,7 @@ func GetIdentityFeatureStates(
5252
}
5353

5454
func GetIdentityFeatureState(
55-
environment *environments.EnvironmentModel,
55+
environment environments.EnvironmentModel,
5656
identity *identities.IdentityModel,
5757
featureName string,
5858
overrideTraits ...*traits.TraitModel,
@@ -68,7 +68,7 @@ func GetIdentityFeatureState(
6868
}
6969

7070
func GetIdentitySegments(
71-
environment *environments.EnvironmentModel,
71+
environment environments.EnvironmentModel,
7272
identity *identities.IdentityModel,
7373
overrideTraits ...*traits.TraitModel,
7474
) []*segments.SegmentModel {
@@ -84,7 +84,7 @@ func GetIdentitySegments(
8484
}
8585

8686
func getIdentityFeatureStatesMap(
87-
environment *environments.EnvironmentModel,
87+
environment environments.EnvironmentModel,
8888
identity *identities.IdentityModel,
8989
overrideTraits ...*traits.TraitModel,
9090
) map[int]*features.FeatureStateModel {

flagengine/engine_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func TestEnvironmentGetFeatureStateFeatureNotFound(t *testing.T) {
153153
assert.Nil(t, fs)
154154
}
155155

156-
func getEnvironmentFeatureStateForFeature(env *environments.EnvironmentModel, feature *features.FeatureModel) *features.FeatureStateModel {
156+
func getEnvironmentFeatureStateForFeature(env environments.EnvironmentModel, feature *features.FeatureModel) *features.FeatureStateModel {
157157
for _, fs := range env.FeatureStates {
158158
if fs.Feature == feature {
159159
return fs

flagengine/flagengine_integration_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func TestEngine(t *testing.T) {
4343
t.Run(strconv.Itoa(i)+":"+c.Identity.CompositeKey(), func(t *testing.T) {
4444
assert := assert.New(t)
4545
require := require.New(t)
46-
actual := flagengine.GetIdentityFeatureStates(&testData.Environment, &c.Identity)
46+
actual := flagengine.GetIdentityFeatureStates(testData.Environment, &c.Identity)
4747
expected := c.Response.Flags
4848

4949
sort.Slice(actual, func(i, j int) bool {

flagengine/utils/fixtures/fixtures.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ func Feature2() *features.FeatureModel {
7777
}
7878
}
7979

80-
func Environment(feature1, feature2 *features.FeatureModel, project *projects.ProjectModel) *environments.EnvironmentModel {
81-
return &environments.EnvironmentModel{
80+
func Environment(feature1, feature2 *features.FeatureModel, project *projects.ProjectModel) environments.EnvironmentModel {
81+
return environments.EnvironmentModel{
8282
ID: 1,
8383
APIKey: "api-key",
8484
Project: project,
@@ -89,7 +89,7 @@ func Environment(feature1, feature2 *features.FeatureModel, project *projects.Pr
8989
}
9090
}
9191

92-
func Identity(env *environments.EnvironmentModel) *identities.IdentityModel {
92+
func Identity(env environments.EnvironmentModel) *identities.IdentityModel {
9393
return &identities.IdentityModel{
9494
Identifier: "identity_1",
9595
EnvironmentAPIKey: env.APIKey,
@@ -104,7 +104,7 @@ func TraitMatchingSegment(segCond *segments.SegmentConditionModel) *traits.Trait
104104
}
105105
}
106106

107-
func IdentityInSegment(trait *traits.TraitModel, env *environments.EnvironmentModel) *identities.IdentityModel {
107+
func IdentityInSegment(trait *traits.TraitModel, env environments.EnvironmentModel) *identities.IdentityModel {
108108
return &identities.IdentityModel{
109109
Identifier: "identity_2",
110110
EnvironmentAPIKey: env.APIKey,
@@ -134,16 +134,16 @@ func MVFeatureStateValue() *features.MultivariateFeatureStateValueModel {
134134
}
135135

136136
func EnvironmentWithSegmentOverride(
137-
env *environments.EnvironmentModel,
137+
env environments.EnvironmentModel,
138138
featureState *features.FeatureStateModel,
139139
segment *segments.SegmentModel,
140-
) *environments.EnvironmentModel {
140+
) environments.EnvironmentModel {
141141
segment.FeatureStates = append(segment.FeatureStates, featureState)
142142
env.Project.Segments = append(env.Project.Segments, segment)
143143
return env
144144
}
145145

146-
func GetFixtures() (*features.FeatureModel, *features.FeatureModel, *segments.SegmentModel, *environments.EnvironmentModel, *identities.IdentityModel) {
146+
func GetFixtures() (*features.FeatureModel, *features.FeatureModel, *segments.SegmentModel, environments.EnvironmentModel, *identities.IdentityModel) {
147147
feature1 := Feature1()
148148
feature2 := Feature2()
149149
org := Organisation()

offline_handler.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import (
88
)
99

1010
type OfflineHandler interface {
11-
GetEnvironment() *environments.EnvironmentModel
11+
GetEnvironment() environments.EnvironmentModel
1212
}
1313

1414
type LocalFileHandler struct {
15-
environment *environments.EnvironmentModel
15+
environment environments.EnvironmentModel
1616
}
1717

1818
// NewLocalFileHandler creates a new LocalFileHandler with the given path.
@@ -29,12 +29,12 @@ func NewLocalFileHandler(environmentDocumentPath string) (*LocalFileHandler, err
2929

3030
// Create and initialise the LocalFileHandler
3131
handler := &LocalFileHandler{
32-
environment: &environment,
32+
environment: environment,
3333
}
3434

3535
return handler, nil
3636
}
3737

38-
func (handler *LocalFileHandler) GetEnvironment() *environments.EnvironmentModel {
38+
func (handler *LocalFileHandler) GetEnvironment() environments.EnvironmentModel {
3939
return handler.environment
4040
}

realtime.go

+21-15
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func (c *Client) startRealtimeUpdates(ctx context.Context) {
1818
panic("Failed to fetch the environment while configuring real-time updates")
1919
}
2020

21-
env := c.environment.GetEnvironment()
21+
env, _ := c.environment.GetEnvironment()
2222
envUpdatedAt := env.UpdatedAt
2323
log := c.log.With("environment", env.APIKey, "current_updated_at", &envUpdatedAt)
2424

@@ -40,37 +40,43 @@ func (c *Client) startRealtimeUpdates(ctx context.Context) {
4040
}
4141
defer resp.Body.Close()
4242

43+
log.Info("connected to realtime")
44+
4345
scanner := bufio.NewScanner(resp.Body)
4446
for scanner.Scan() {
4547
line := scanner.Text()
46-
if strings.HasPrefix(line, "data: ") {
47-
parsedTime, err := parseUpdatedAtFromSSE(line)
48-
if err != nil {
49-
log.Error("failed to parse realtime update event", "error", err, "raw_event", line)
50-
continue
51-
}
52-
if parsedTime.After(envUpdatedAt) {
53-
log.WithGroup("event").Info("received update event",
48+
parsedTime, err := parseUpdatedAtFromSSE(line)
49+
if err != nil {
50+
log.Error("failed to parse realtime update event", "error", err, "raw_event", line)
51+
continue
52+
}
53+
if parsedTime.After(envUpdatedAt) {
54+
log.WithGroup("event").
55+
Info("received update event",
5456
slog.Duration("update_delay", parsedTime.Sub(envUpdatedAt)),
5557
slog.Time("updated_at", parsedTime),
5658
slog.String("environment", env.APIKey),
5759
)
58-
err = c.UpdateEnvironment(ctx)
59-
if err != nil {
60-
continue
61-
}
62-
envUpdatedAt = parsedTime
60+
err = c.UpdateEnvironment(ctx)
61+
if err != nil {
62+
log.Error("realtime update failed", "error", err)
63+
continue
6364
}
65+
envUpdatedAt = parsedTime
6466
}
6567
}
6668
if err := scanner.Err(); err != nil {
67-
c.log.Error("failed to read from realtime stream", "error", err, "stream_url", &resp.Request.URL)
69+
log.Error("failed to read from realtime stream", "error", err)
6870
}
6971
}
7072
}
7173
}
7274

7375
func parseUpdatedAtFromSSE(line string) (time.Time, error) {
76+
if !strings.HasPrefix(line, "data: ") {
77+
return time.Time{}, nil
78+
}
79+
7480
var eventData struct {
7581
UpdatedAt float64 `json:"updated_at"`
7682
}

0 commit comments

Comments
 (0)