-
Notifications
You must be signed in to change notification settings - Fork 4
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
feat: Client provider guidelines #14
base: main
Are you sure you want to change the base?
Conversation
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.
couple minor typos/grammar suggestions
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.
Great start, thanks! I added a few suggestions but I think this will be helpful.
We may want to avoid calling it a spec (at least for now). I would recommend calling it a guideline.
provider/specs/client.md
Outdated
The endpoint will return a list of configuration for the provider: | ||
- `name`: Name of the flag management system, it should be used in the `metadata.name` name (ex: `OFREP Web Provider ${metadata.name}`). | ||
- `capabilities`: List of capabilities of the flag management system and their associated configuration. *Check the [capabilities section](#capabilities) for the details of each capability.* | ||
|
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.
What's the expected behavior if the call to /ofrep/v1/configuration
fails?
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.
Should fallback to the defaults and I think we should define the default behaviour in this spec
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.
Hum good question, we were wondering with @lukas-reining and I think we should put the provider in error and retry later (probably with an exponential backoff).
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.
Will calling the configuration API be an opt-in feature? If it's explicitly enabled by the user, then putting the provider into an error state may make sense. Otherwise, I don't think it should.
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.
If we want to configure it, I will be more in favour of having an opt-out rather than an opt-in.
The reasoning is that this will encourage vendors to provide the information on how it works but also to avoid using the default values, the information of this endpoint is here to help to work better with the flag management system, not using them by default will be a bit weird from my point of view.
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 will affect initial page load times, and the config API doesn't provide anything (at least not yet) that couldn't be defined explicitly by the OFREP constructor.
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 @thomaspoignant for getting started on this 🙌
I have following general remarks,
- Language - Given this is a spec and to be similar to OF Spec, I think we should generilize langauge to avoid "we" usage (ex:-
In this specification we will specify..
toThis specificaiton specify..
) - Spec name & title - Given this is specific to client side (aka static context), a good name for file could be
client-provider
/static-context-provider
Otherwise I think this is a great first step
623950c
to
5c18eb8
Compare
I have renamed the file and stopped using the term specification to avoid any confusion here. |
Thanks :)
Yeah, can start simple and then convert this to a spec. So let's not spend too much time on 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 am happy with this so we can get started :)
An implementation of an OFREP client provider should start with an initialization of the provider. | ||
|
||
The `initialize()` function should follow those steps: | ||
1. Make a GET request to the `/ofrep/v1/configuration` endpoint to retrieve the configurations return by the flag management system and store them in memory to be available for all the function of the provider. *(See [Annexe 1](#annexe-1) for the description of the endpoint response)* |
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 this step be optional? The provider should be able to work without having to call the configuration API.
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 also my general feeling, that this should be an opt-in feature as @beeme1mr says.
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 was more thinking toward something that can be enabled by default and opt-out on demand.
See #14 (comment)
##### Polling | ||
`polling`: define how the provider should do the polling | ||
|
||
- `enabled`: if `true` the provider should poll the `/ofrep/v1/evaluate/flags` regularly to check if any flag evaluation has changed in the flag management system. |
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 isn't the current behavior in the OFREP web provider.
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.
OFREP web provider by default is doing polling.
You have a default value set if no pollingInterval
is provided during the initialization.
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 start and it is really valuable to have!
I left some comments, and I would generally tent to describe the expected behavior of the provider in third person instead of using first person developer.
An implementation of an OFREP client provider should start with an initialization of the provider. | ||
|
||
The `initialize()` function should follow those steps: | ||
1. Make a GET request to the `/ofrep/v1/configuration` endpoint to retrieve the configurations return by the flag management system and store them in memory to be available for all the function of the provider. *(See [Annexe 1](#annexe-1) for the description of the endpoint response)* |
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 also my general feeling, that this should be an opt-in feature as @beeme1mr says.
- If the endpoint returns an error, the `initialize()` function must error and exit. | ||
- If the request is successful, we should store in a local cache all of the flags evaluation results returned by the API in a local cache. We should also store the `ETag` header in the provider to be able to send it back later. |
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 would be better to leave the we
out and talk in third person about the provider.
The evaluation should not perform any remote API calls. | ||
|
||
When calling an evaluation function the provider should check if the associated type is supported, by checking the key `capabilities.flagEvaluation.unsupportedTypes` from the configuration endpoint. | ||
- If the type is unsupported, we should exit early and directly return an 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.
Do we know which error we want to throw here? I think it would be nice to specify here.
Maybe we can even have a new Error type, the case that a system does not support a type happens pretty often. (independently from OFREP)
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.
Yes, this is true, should we add that to the OF spec?
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.
Mh yes I think we should add it to the OF spec.
I could open a PR next days, what do you think @thomaspoignant ?
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 would be awesome
```mermaid | ||
flowchart TD | ||
A[evaluation\nfunction] --> B{Is function type in\nunsupportedTypes?} | ||
B --> |YES| C(return an error) | ||
B --> |NO| D{Is flag key stored\nin local cache?} | ||
D --> |NO| E(return a FlagNotFound error) | ||
D --> |YES| F{Is cached evaluation\nresponse in error?} | ||
F --> |YES| G(Map the error as a\nprovider error and return) | ||
F --> |NO| H{Is the flag value the\nsame type as the\nevaluation function?} | ||
H --> |YES| I(return the evaluation response) | ||
H --> |NO| J(return a TypeMismatch 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.
Love 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.
Yes, this is excellent!
|
||
In the constructor, the provider should check if the `baseURL` is a valid URL and return an error if the URL is invalid. | ||
|
||
## Initialize the provider |
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 a bit torn here wether my thinking is going to deep or the use case is too fringe but bare with me:
Some applications have strict rules or goals of how fast they need to be able to show their initial screen. These apps may not have time (or do not want) to wait for network requests to finish before considering a feature flagging SDK to be initialised.
Obviously there are many ways one can solve this, but we have written mobile providers with disk persistence where we then can pass a FetchingStrategy
to the constructor where the app owner can decide wether or not they want the initialize()
function to block until fresh data is available or simply load any previous data from disk (and then refresh from the backend in the background).
Firebase Remote Config explains the concepts 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.
I like the idea of various initialization strategies and the link to Firebase Remote Config is a great resource.
Perhaps one way to implement that would be to put this section under a "Fetch on load" strategy. We could start with a single strategy (and perhaps make it the default) but that would give us the ability to add additional strategies to support specific use cases.
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.
Interesting point here. I see the value of having multiple fetching strategies but I am not sure how to frame that into this document.
I see 2 options:
- We try to define the different possible ways of doing the initialisation for the OFREP client providers, and we add them to the document.
- We remove that block since there is some chance that it could be different to initialize something depending on the platform (web/mobile).
Do you have any preference?
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 thinking that it might be most reasonable to ignore my comment about this and go ahead with a blocking network call as part of the initialize function in order to not block this document.
If we believe that the topic warrants additional discussion we can open an issue to discuss it there and amend the spec or add an appendix. wdyt?
7859893
to
a61e2bf
Compare
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Mark Phelps <209477+markphelps@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Signed-off-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Michael Beemer <beeme1mr@users.noreply.github.com> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Lukas Reining <lukas.reining@codecentric.de> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
Co-authored-by: Lukas Reining <lukas.reining@codecentric.de> Signed-off-by: Thomas Poignant <thomas.poignant@gmail.com> Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
ed3ac70
to
961f3c7
Compare
This PR is a first draft for a provider client spec.
Please provide feedback on this one to know if I am heading in the right direction and to know if it can be helpful to create providers.
Those are the guidelines related to the issue #11