-
Notifications
You must be signed in to change notification settings - Fork 10
fix: allow direct fetching of feature flags [IDE-1106] #322
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?
Conversation
Previously all feature flags had to be registered with the GAF configuration before they could be used.
I need this for https://github.com/snyk/snyk-ls/pull/820/files#diff-d74ed587c3dae58e0c1ddc2a0eec71bdb58552ef0b6852d664fbb849e868ec19R59 so I can directly fetch feature flags using GAF's API. config_utils.CurrentFeatureFlagChecker().GetFeatureFlag(config.CurrentConfig().Engine(), ffStr) I needed to mock the fetching of feature flags for the test (https://github.com/snyk/snyk-ls/pull/820/files#diff-29de3939a8441248dd44aa2f2eb09f23a57909cf126d85840d0a1519580e25f3R37), which is why I did the horrible stuff with and interface and struct. If there is a better way to do it, then please let me know, it's my first time using mocks in Go. |
Question: I think I'm not yet understanding the use case or the problem you are solving well enough. Could you please explain this a bit more? |
Certainly, for some context, in the language server we have the LSP command Now I am trying to rewrite the config_utils.AddFeatureFlagToConfig(engine, configuration.FF_CODE_CONSISTENT_IGNORES, "snykCodeConsistentIgnores") For the Now whether this is the best solution for bypassing the internal config key names or not, I don't know. The code was a lot simpler before I added the ability to mock the response so that I could get the unit test for the Another option would be to add a function |
Thank you @rrama ! That is very helpful. |
currentFFC = ffc | ||
} | ||
|
||
func (ffc *featureFlagCheckerImpl) GetFeatureFlag(engine workflow.Engine, featureFlagName string) (bool, error) { |
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.
Suggestion: The singleton construct is not really used here, so I would recommend to just have a method GetFeatureFlag()
without the whole singleton.
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 went for the overridable singleton solution like we do in
func (e *EngineImpl) SetConfiguration(config configuration.Configuration) { |
because this seemed to me to be the way to allow for mocking. If there is a better way that means I can go back to just having the one function, then that would be good.
For reference, here is how I am able to use the mocking with the overridable singleton solution:
snyk/snyk-ls@e94e241#diff-29de3939a8441248dd44aa2f2eb09f23a57909cf126d85840d0a1519580e25f3R46
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.
maybe instead of a singleton the fetureFlagChecker can be set on the engine lvl engine.SetFeatureFlagChecker
. or smth similar ? That way tests and other clients can set the mock.
cc @PeterSchafer
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.
sorry, it seems I missed any notification on this thread. is this still something we are looking to merge?
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.
@PeterSchafer Yes, still looking to merge, but there isn't a rush for it anymore, so can be cooldown. Would you prefer I set it on the function on the engine level as Shawky suggested?
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.
Haha, I see, everyone already commented on that. I think it's a bit overengineered. Having an interface for mocking is fine, but IMHO a singleton is not needed. I'd implement the interface in Configuration as it's in the configuration domain, not the engine.
Just to summarize my understanding. Goal: We want to enable accessing feature flags whose names are not yet known at compile time.
I think there is a misunderstanding. the configuration key and the feature flag name can be the same, there is no such thing as an internal key required. Only if you want to support setting a feature flag remotely and via other means like environment variables, a mapping is helpful. But this is not what you need.
I hope this makes sense @rrama. If I'm missing something here, please let's jump on a call to get to a resolution. |
} | ||
|
||
var ( | ||
currentFFC FeatureFlagChecker |
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 like that we introduce a global variable here. Why is it needed? Can't we just have an instance in the configuration that is created when the configuration is created?
func CurrentFeatureFlagChecker() FeatureFlagChecker { | ||
mutex.RLock() | ||
defer mutex.RUnlock() | ||
if currentFFC == nil { | ||
currentFFC = &featureFlagCheckerImpl{} | ||
} | ||
return currentFFC | ||
} | ||
|
||
func SetCurrentFeatureFlagChecker(ffc FeatureFlagChecker) { | ||
mutex.Lock() | ||
defer mutex.Unlock() | ||
currentFFC = ffc | ||
} |
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 do we need a singleton pattern?
|
||
func (ffc *featureFlagCheckerImpl) GetFeatureFlag(engine workflow.Engine, featureFlagName string) (bool, error) { | ||
config := engine.GetConfiguration() | ||
httpClient := engine.GetNetworkAccess().GetHttpClient() |
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 wonder whether it would be better to have config & httpclient injected, to decouple and avoid side-effects (same like it's done with apiClient).
//go:generate $GOPATH/bin/mockgen -source=feature_flag.go -destination ../../mocks/feature_flag.go -package mocks -self_package github.com/snyk/go-application-framework/pkg/local_workflows/config_utils | ||
|
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.
nice :)
I am going to move this to a draft for now until the next cooldown, then I will look closer at the solutions suggested and then we can pick which works best. |
Since we might need the same functionality in another use case, I went ahead and opened #349 which should do what you need. Let me know if this makes sense. |
Previously all feature flags had to be registered with the GAF configuration before they could be used.