open-feature / js-sdk

JavaScript SDK for OpenFeature
https://openfeature.dev
Apache License 2.0
168 stars 32 forks source link

[FEATURE] Angular SDK implementation #976

Closed juanparadox closed 1 month ago

juanparadox commented 2 months ago

Requirements

We should implement an Angular SDK. The SDK would provide an "Angular-friendly" API and features, built on top of the Web SDK.

The list is not conclusive and is up for discussion, including the removal, update, or addition to this list:

I'm a little rusty with Angular since React is more my space, but I would like some opinions on how the subscription part of the developer experience should work. Reason being is because with my suggestions above, the user has to subscribe to a client inside of every consumer - is this the best way to allow the user to evaluate a value for a flag or can we do this a better way with Angular?

Another point to keep in mind is whether to build this using signals or observables?

Non-functional requirements:

lukas-reining commented 2 months ago

Hey @juanparadox great that you started on this! I have some questions:

Where is the difference between these two requirements? Do you have an example for the second one?

For the injection I could imagine injecting the client being good enough. We do something similar in the Nest SDK: https://github.com/open-feature/js-sdk/tree/main/packages/nest#usage

Maybe we could also add an injection of individual flag values e.g. as an observable? We do something similar with flag value promises in the Nest SDK. But with the static context we could maybe go for flag value observables that update automatically?

An injectable service which will allow for the exposure of OpenFeature methods such as setProvider and setContext and for a way to get the provider's status

In the Nest SDK we do this only on module init, but I think a thin injectable service to provide that functionality could be good.

juanparadox commented 2 months ago

Hey @juanparadox great that you started on this! I have some questions:

Where is the difference between these two requirements? Do you have an example for the second one?

  • A way to subscribe to a client so that the user can get a flag's value.
  • Add handlers for when the context changes so that the subscribed client provides new flag values

For the injection I could imagine injecting the client being good enough. We do something similar in the Nest SDK: https://github.com/open-feature/js-sdk/tree/main/packages/nest#usage

Maybe we could also add an injection of individual flag values e.g. as an observable? We do something similar with flag value promises in the Nest SDK. But with the static context we could maybe go for flag value observables that update automatically?

An injectable service which will allow for the exposure of OpenFeature methods such as setProvider and setContext and for a way to get the provider's status

In the Nest SDK we do this only on module init, but I think a thin injectable service to provide that functionality could be good.

Hey! For the differences between these two:

The main difference is that the second item is not something that the consumer needs to subscribe to, rather, the service just ensures that it's initialized context change handlers which then update the OpenFeature provider's client so that the subscribers receive a new client which now has the newly evaluated values. Basically, the first is just a simple subscription to the client() from OpenFeature and the second it ensuring the consumer gets new flag values when the context changes.

Couple of follow-ups for you:

Maybe we could also add an injection of individual flag values e.g. as an observable?

What would that look like for a consumer/subscriber? Something like this?

this.openFeatureService.getBooleanValue('flag-key', false).subscribe(...)

But with the static context we could maybe go for flag value observables that update automatically?

That update automatically if the context changes? The reason I ask about this clarification is to differentiate for cases where the flag config is set to stream the value vs requiring a context change handler/page or component refresh.

beeme1mr commented 2 months ago

I found this article that talks about using directives to support feature flags in templates. Overall, the design looks quite nice in my opinion. I could try and reach out to the author if that would be useful. https://medium.com/@iamjustin/feature-flags-in-angular-d50a2b8437fe

I posted this in Slack but thought it would be more useful in this thread.

toddbaert commented 2 months ago

@lukas-reining @juanparadox I think injecting a client is a good idea, but IMO a directive would be a really "Angular-esque" approach. It would be great if we could build one that would support the same sort of built-in re-rendering we have in the React SDK. I think this could be done by subscribing to change events and then marking the component for re-check.

Overall, I think and Angular SDK would be highly valuable. Recently our React SDK (just React, not the web SDK!) surpassed the backend JS SDK in downloads. People REALLY want client-side SDKs and I think this one would get a lot of usage. I personally have found that many orgs which use .NET on the backend happen to use Angular on the frontend, and we already have signigicant .NET usage as well.

lukas-reining commented 2 months ago

Hey @juanparadox, so let me try to describe how I understood it:

For the differences between these two: A way to subscribe to a client so that the user can get a flag's value. Add handlers for when the context changes so that the subscribed client provides new flag values The main difference is that the second item is not something that the consumer needs to subscribe to, rather, the service just ensures that it's initialized context change handlers which then update the OpenFeature provider's client so that the subscribers receive a new client which now has the newly evaluated values. Basically, the first is just a simple subscription to the client() from OpenFeature and the second it ensuring the consumer gets new flag values when the context changes.

So this means the first is just for getting the value once? Why do we have to subscribe here? The web SDK is already synchronous so I think a component could e.g. just call OpenFeatureService.getBooleanValue(...):boolean.

The second option would provide is with something like OpenFeatureService.getBooleanObservable(...):Observable<boolean> which is subscribed to the event handlers of the internal client so it always emits the newest value? This way a component can always react to flag changes if needed.

Is this what you meant or did I get it wrong?

Couple of follow-ups for you:

Maybe we could also add an injection of individual flag values e.g. as an observable?

I think this would be better done with the directives, this idea was way better.

But with the static context we could maybe go for flag value observables that update automatically?

That update automatically if the context changes? The reason I ask about this clarification is to differentiate for cases where the flag config is set to stream the value vs requiring a context change handler/page or component refresh.

I think the two options I described in the top handle these two cases.

lukas-reining commented 2 months ago

@beeme1mr @toddbaert the directive is a great idea! The post you linked @beeme1mr evaluates once on page load, but the general structure is cool! We could add something like the following to also enable automatic rerenders on flag updates: https://nils-mehlhorn.de/posts/angular-observable-directive/

What do you think @juanparadox?

toddbaert commented 2 months ago

That post is super helpful @lukas-reining . I think it's a great starting point.

lukas-reining commented 2 months ago

Overall, I think and Angular SDK would be highly valuable. Recently our React SDK (just React, not the web SDK!) surpassed the backend JS SDK in downloads. People REALLY want client-side SDKs and I think this one would get a lot of usage. I personally have found that many orgs which use .NET on the backend happen to use Angular on the frontend, and we already have signigicant .NET usage as well.

I totally think so too @toddbaert! .NET or Spring Boot with Angular is a really common stack for orgs around here.

juanparadox commented 2 months ago

@lukas-reining The automatic rerender directive sounds awesome and would actually remove the approach I was suggesting that messes with having to subscribe to things like the client.

Just to address OpenFeatureService.getBooleanValue(...):boolean for:

Why do we have to subscribe here?

I was approaching this from the point of view that the UI component would have to subscribe to something for the rendered UI to update whenever the flag changes. If the context changes will automatically update the value with the directive approach then this is no longer a concern and the directive approach feels easier to manage and thus a better developer experience 👍

lukas-reining commented 2 months ago

Cool! How do we continue from here? Does someone want to start a PoC/Draft for the SDK? @juanparadox @toddbaert I can do that too, would just have to see when I can start that.

toddbaert commented 2 months ago

Honestly I've been working on a lot of our internal OpenFeature stuff at work, so I don't have the time to take on something so big myself at the moment. I would support anyone who wants to work on this though. I have a decent amount of Angular experience I would be happy to lend in reviewing and testing.

lukas-reining commented 2 months ago

@juanparadox not sure if you want to do it? Otherwise I could come up with a first version I guess🤔

juanparadox commented 2 months ago

@lukas-reining please feel free to take a stab at it!

Smarmellata commented 2 months ago

Hi guys, I think this issue is very useful and I'm interested to follow it. I am currently implementing a feature flag system inside an angular app and I am trying to get advantage from dependency injection to have specific providers for specific lazy loaded features.

At the moment i am exactly struggling on how create a new provider at the start of my lazy loading feature and could be that my "angular approach" to the usage of the js-sdk has some limitation.

So in my scenario i have a service that wraps the methods of the "OpenFeature" class and i use it to store data in it (that sticks to the module that injects it) and customize the provider for each feature.

I think that in generale wrapping external libs usage into custom services is recommended especially if you work with a big application.

About possibility to use different domains how did you thought to manage the different clients in this Angular SDK?

Smarmellata commented 2 months ago

@lukas-reining The automatic rerender directive sounds awesome and would actually remove the approach I was suggesting that messes with having to subscribe to things like the client.

Just to address OpenFeatureService.getBooleanValue(...):boolean for:

Why do we have to subscribe here?

I was approaching this from the point of view that the UI component would have to subscribe to something for the rendered UI to update whenever the flag changes. If the context changes will automatically update the value with the directive approach then this is no longer a concern and the directive approach feels easier to manage and thus a better developer experience 👍

In general i think that the scenario in which the flag is returned as a boolean and not as an observable is quite cool.

Not all the situation requires an automatic change of the feature flag value, sometime is something used "one shot" so both approaches could be useful.