-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
ExPlat: Handle local Experiment registration #14559
Conversation
You can trigger optional UI/connected tests for these changes by visiting CircleCI here. |
You can test the changes on this Pull Request by downloading the APK here. |
@@ -183,6 +185,7 @@ | |||
@Inject AppConfig mAppConfig; | |||
@Inject ImageEditorFileUtils mImageEditorFileUtils; | |||
@Inject ExPlat mExPlat; | |||
@Inject Set<Experiment> mExperiments; |
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'm wondering whether it would make sense to inject this directly in the ExPlat
class. The reason is that it would hide this internal piece of logic in there instead of using it in the shared application class. I see it's a singleton but I wouldn't expect it to outlive the application class. WDYT?
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 would make sense, for sure. The reason I avoided doing that in the first place was because I didn't want to generate a circular dependency (ExPlat
depends on Experiment
that depends on ExPlat
).
That said, I tried using dagger.Lazy
to inject the Set<Experiment>
in the ExPlat
constructor and it actually ended up working pretty well and the code got cleaner, specially because we no longer need the ExPlat::start
method.
You can check the changes here: 3eb27b7 – thanks for the suggestion!
@Multibinds fun experiments(): Set<Experiment> | ||
|
||
// Copy and paste the line below to add a new experiment. | ||
// @Binds @IntoSet fun exampleExperiment(experiment: ExampleExperiment): Experiment |
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.
Is it viable to introduce some kind of a lint rule that would fail if there is an experiment that is not registered in the module? Maybe that's unnecessary.
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 probably viable, although I have no experience with custom lint rules so I can't say for sure.
That said, if that's something we want to do, we should probably do it in a different PR (specially since I guess it would need to be submitted to WordPress-Lint-Android?).
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's looking good and works well, thanks for the PR! I have one comment 👍
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.
Thanks for the changes, it's looking good 👍
This PR is a follow up to #14498 and is related to wordpress-mobile/WordPress-FluxC-Android#1970.
These changes can be divided into two parts:
Experiment
registration in theExPlat
class.Experiment
s to theExPlat
class for registration.I thought about splitting this PR in two, but its changes are not so big overall and I think they make sense together, but let me know what you prefer.
Part 1
In this first part, the
ExPlat
class was updated in two ways:start()
method that replaces the direct call we previously made toforceRefresh()
once the app started. This method accepts a set ofExperiment
s, maps it to a list of experiment names which is then held internally and then callsforceRefresh()
.forceRefresh()
andrefreshIfNeeded()
, we check if the list is empty and if so, we prevent any calls to the API from being made.getVariation()
we check if the list contains the name of the providedExperiment
and if it doesn't, we just returnControl
directly (ifBuildConfig.DEBUG
istrue
, we throw aIllegalArgumentException
instead).Part 2
In this part, Dagger is being used to actually provide the set of
Experiment
s to theExPlat
class. Since ourExperiment
implementations were already being included in the dependency graph, I just created aExperimentModule
where we can add the experiments we want to register and bind them all into aSet<Experiment>
by using a combination of@Binds
and@IntoSet
annotations. It also uses a@Multibinds
annotation to provide an emptySet
in case noExperiment
is currently registered.This approach is a bit similar to the one we use to provide a
Map
ofViewModel
s to theViewModelFactory
class and it introduces a similar extra step. To demonstrate, here's how it would work in practice to add a newExperiment
:Experiment
class, and inject theExPlat
dependency using Dagger, just like we did before:Experiment
in theExperimentModule
like so:And that's it. I also created an
ExampleExperiment
and left a commented out line inExperimentModule
with the code above as an example.👉 Note: while this approach is pretty safe to use, I don't expect it to be final. Once we start working on adding functionality to list and edit registered experiments locally (as described in #14089), I expect we should be able to replace it with something similar to what we currently have with our remote configs, where we would just need to add an annotation to our
Experiment
class and the generated code would handle the rest for us. For now, other than that extra step, I don't see any major drawbacks that would prevent us from moving forward with this, so please let me know if you do!To test
Without registered experiments
logcat
, make sure you don't see any messages like:ExPlat: fetching assignments successful with result: ...
.Try logging in/out and restarting the app and make sure you still don't see the message.
With registered experiments
ExperimentModule
class and restart the app.logcat
, notice a message like:ExPlat: fetching assignments successful with result: ...
.Regression Notes
Potential unintended areas of impact
n/a
What I did to test those areas of impact (or what existing automated tests I relied on)
n/a
What automated tests I added (or what prevented me from doing so)
Added a few extra unit tests to account for new behaviors.
PR submission checklist:
RELEASE-NOTES.txt
if necessary.