-
Notifications
You must be signed in to change notification settings - Fork 15
feat!: v5 #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat!: v5 #151
Conversation
a050a54
to
e79acfd
Compare
33aefe0
to
bcc3e1c
Compare
6394d25
to
cf672ff
Compare
b38875c
to
fb512ba
Compare
fb512ba
to
d0e37a1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the overall approach and didn't find any immediate issues having read through the code. That said, I expect the ability to provide transient traits/identities to be restored in a separate PR, preferably ahead of next release — we can discuss the exact approach offline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have gone through some of the code, but not all of it yet.
} | ||
|
||
type EnvironmentEvaluationContext struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did we get rid of this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO it doesn't provide any value to the SDK, and we should encourage customers to create evaluation contexts using our functions such as NewEvaluationContext
. I can't see any reason why it should have been here in the first place. I'm happy to add it back if we can find a good reason to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was used to dynamically pass environment key to get flags etc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That can and should be passed as the argument to GetFlags
. I really would like to not have the environment be mutated by SDK users.
In general, the evaluation context for server-side SDKs should not be mutable, since there's almost surely going to be a single instance in the whole application performing all the evaluations. I think this is the same reason why OpenFeature server-side SDKs don't provide a way to mutate the context, and instead require that each flag evaluation have an explicit context. I agree with this approach.
// Initialise analytics processor | ||
if c.config.enableAnalytics { | ||
c.analyticsProcessor = NewAnalyticsProcessor(c.ctxAnalytics, c.client, c.config.baseURL, nil, c.log) | ||
if c.ctxAnalytics != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add the variable back? I'd prefer not to do if else on context
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also a test here to make sure if analytics processor is enabled, it is initialised properly?
if ok { | ||
envCtx := ec.Environment | ||
if envCtx != nil { | ||
req.SetHeader(EnvironmentKeyHeader, envCtx.APIKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@khvn26 do you remember why we did this? As far I can remember, it was because a customer wanted it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was just general support for overriding the evaluation context
@@ -205,92 +210,100 @@ func (c *Client) BulkIdentify(ctx context.Context, batch []*IdentityTraits) erro | |||
SetBody(&body). | |||
SetContext(ctx). | |||
ForceContentType("application/json"). | |||
Post(c.config.baseURL + "bulk-identities/") | |||
if resp.StatusCode() == 404 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did we get rid of this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any value in handling this error. A 404 could be returned by an intermediate proxy so the suggestion to make sure Edge is being used is not always correct.
The better solution here would be to implement the /bulk-identities
endpoint on the Core API, but have it return 404 and a relevant error message.
@@ -7,34 +7,26 @@ import ( | |||
"github.com/Flagsmith/flagsmith-go-client/v4/flagengine/environments" | |||
) | |||
|
|||
type OfflineHandler interface { | |||
GetEnvironment() *environments.EnvironmentModel | |||
type Environment interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just confusing—now we have an interface with the same name as the method. I don't really think we're gaining anything by breaking the old interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name "Handler" is misleading. This type was not being used as a handler because it's not called in response to any event; the offline environment was only being initialised when the client is constructed and never again. For example, http.Handler
is named correctly because it's called in response to a request, but this was not the case for OfflineHandler
.
I'll rename the method in this interface back to GetEnvironment
return nil | ||
} | ||
|
||
func (cs *environmentState) SetEnvironment(env *environments.EnvironmentModel) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have any tests for functions defined in the file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not directly, I'll add them later
} | ||
return f | ||
} | ||
if resp.StatusCode() != 200 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did we get rid of this check? Anything other than 200 should be error for us?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wasn't removed, just changed to !resp.IsSuccess
. The diff is confusing because this method was moved in the file. The check is still there: https://github.com/Flagsmith/flagsmith-go-client/pull/151/files/7dcb4a00171dc3666b2a770a7e573730f9fe9a80#diff-4b667feae66c9d46b21b9ecc19e8958cf4472d162ce0a47ac3e8386af8bbd8cfR177
|
||
analyticsProcessor *AnalyticsProcessor | ||
baseURL string | ||
timeout time.Duration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did we move timeout and proxy out of options and set them directly on the client? Are we planning to use them somewhere else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just convenience, I didn't feel it was worth having a container type to hold the different settings so I've added them directly to the client. I can revert if you feel strongly about it but I don't think it makes a difference either way.
client_test.go
Outdated
|
||
flags, err := client.GetFlags(ctx, nil) | ||
flags, err := client.GetEnvironmentFlags(context.Background()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TestGetFlags
is testing GetEnvironmentFlags
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
c9e8ea2
to
7f3bd06
Compare
Breaking changes
Removed support for transient traits and transient evaluations
Traits are key-value pairs, so they are most naturally represented by a
map[string]any
. However, because each individual transient can be transient or not, we have to introduce a newTrait
type to account for this, so we lose the simplicity and interoperability of using plain maps. The same applies for identifiers, which could be represented by a nativestring
but instead we have to use anIdentity
struct or awkwardly pass around booleans.To me, this tradeoff is not worth it, because this SDK will almost always be used with local evaluation, i.e. all evaluation is transient anyway. Transient identities can still be used with an empty identifier.
More thoughts on transient traits
I think the concept of transient traits and identities is flawed and should be replaced with something simpler. I also think this is the reason why supporting transient traits in SDKs causes the code to be so cumbersome and awkward.
An evaluation context is some data that should know nothing about when or how it's being evaluated. The fact that Flagsmith can sometimes persist context data as a side effect of remote evaluation does not belong as part of the evaluation context. These are orthogonal concerns that should be kept separate.
For example, the /identities API could accept an allowlist and denylist of trait keys to persist, separate from marking each individual trait as transient or not. This avoids tying trait data and whether to store this data as part of the same entity.
Simplified evaluation context types
NewEvaluationContext
andNewTransientEvaluationContext
are now the only ways to obtainEvaluationContext
instances. All other related types such asIdentity
,IdentityEvaluationContext
,TraitEvaluationContext
andEnvironmentEvaluationContext
are now removed.TraitKey
andTraitValue
are renamed toKey
andValue
respectively in theTrait
struct. Usingmap[string]interface{}
to represent traits is preferred.Removed
WithOfflineHandler
in favour ofWithOfflineEnvironment
Previously,
Client
required two options for using offline mode:WithOfflineMode
andWithOfflineEnvironment
, which restricted with other SDK options could be used. Also, the idea of an offline handler was misleading, because the handler was only ever called once during SDK initialisation, and never again during the SDK lifecycle.Now, we've added
WithOfflineEnvironment
which accepts a constructedEnvironmentModel
instead.NewLocalFileHandler
was removed in favour ofReadEnvironmentFile
, which reads a file into anEnvironmentModel
.We also added a
SetEnvironment
method which lets consumers of the SDK update their offline/local environment whenever they want, instead of whenever the SDK decides to.Simplified error types
Removed
FlagsmithClientError
since it only wraps string messages and is not used in any error handlers.Renamed
FlagmithAPIError
toAPIError
. RemovedMsg
,ResponseStatusCode
andResponseStatus
. AddedAPIError.Response
, which should be used instead.NewClient no longer panics
NewClient
can now return an error and does not panic. AddedMustNewClient
, which can panic.Removed custom logger interface
WithLogger
now accepts a*slog.Logger
instead of a custom logger implementation.New features
The SDK now uses slog for all its logging. All network requests, responses, durations, failures and errors can now be debug logged. Many logs were improved with useful context and more helpful messages.
WithDefaultFlagHandler
now automatically setsFlag.IsDefault
andFlag.FeatureName
. Previously, handlers would have to do this manually, which is unnecssary.When using the SDK offline, the local environment can be now updated at any time by calling
Client.SetEnvironment
instead of only once afterNewClient
is called.Bug fixes
Fixed environments being unnecessarily fetched when the realtime service responds immediately with the latest environment timestamp after a connection starts.
Other changes
GetEnvironmentFlags
andGetIdentityFlags
are no longer deprecated. These are useful aliases for the most common use cases across all SDKs.The current environment state is now stored using a mutex and a
sync.Map
instead ofatomic.Value
references stored in context values. This makes concurrency bugs less likely and is more idiomatic.EvaluationContext
is now entirely defined in this package, and not code-generated from a common schema.All tests now print debug logs by default.