Giter Club home page Giter Club logo

unleash-client-dotnet's Introduction

What is Unleash?

Unleash is a powerful open source solution for feature management. It streamlines your development workflow, accelerates software delivery, and empowers teams to control how and when they roll out new features to end users. With Unleash, you can deploy code to production in smaller, more manageable releases at your own pace.

Feature flags in Unleash let you test your code with real production data, reducing the risk of negatively impacting your users' experience. It also enables your team to work on multiple features simultaneously without the need for separate feature branches.

Unleash is the most popular open source solution for feature flagging on GitHub. It supports 15 official client and server SDKs and over 15 community SDKs. You can even create your own SDK if you wish. Unleash is compatible with any language and framework.


Getting Started with Unleash

1. Setting Up Unleash

To get started with Unleash, you need git and docker installed on your machine.

Execute the following commands:

git clone [email protected]:Unleash/unleash.git
cd unleash
docker compose up -d

Then point your browser to localhost:4242 and log in using:

  • username: admin
  • password: unleash4all

If you'd rather run the source code in this repo directly via Node.js, see the step-by-step instructions to get up and running in the contributing guide.

2. Connect your SDK

Find your preferred SDK in our list of official SDKs and import it into your project. Follow the setup guides for your specific SDK.

If you use the docker compose file from the previous step, here's the configuration details you'll need to get going:

  • For front-end SDKs, use:
    • URL: http://localhost:4242/api/frontend/
    • clientKey: default:development.unleash-insecure-frontend-api-token
  • For server-side SDKs, use:
    • Unleash API URL: http://localhost:4242/api/
    • API token: default:development.unleash-insecure-api-token

If you use a different setup, your configuration details will most likely also be different.

Check a feature toggle

Checking the state of a feature toggle in your code is easy! The syntax will vary depending on your language, but all you need is a simple function call to check whether a toggle is available. Here's how it might look in Java:

if (unleash.isEnabled("AwesomeFeature")) {
  // do new, flashy thing
} else {
  // do old, boring stuff
}

Run Unleash on a service?

If you don't want to run Unleash locally, we also provide easy deployment setups for Heroku and Digital Ocean:

Deploy to Heroku Deploy to DigitalOcean

Configure and run Unleash anywhere

The above sections show you how to get up and running quickly and easily. When you're ready to start configuring and customizing Unleash for your own environment, check out the documentation for getting started with self-managed deployments, Unleash configuration options, or running Unleash locally via docker.


Online demo

Try out the Unleash online demo.

The Unleash online demo


Community and help — sharing is caring

We know that learning a new tool can be hard and time-consuming. We have a growing community that loves to help out. Please don't hesitate to reach out for help.

Join Unleash on Slack

💬 Join Unleash on Slack if you want ask open questions about Unleash, feature toggling or discuss these topics in general.

💻 Create a GitHub issue if you have found a bug or have ideas on how to improve Unleash.

📚 Visit the documentation for more in-depth descriptions, how-to guides, and more.

📖 Learn more about the principles of building and scaling feature flag solutions.


Contribute to Unleash

Unleash is the largest open source feature flag solution on GitHub. Building Unleash is a collaborative effort, and we owe a lot of gratitude to many smart and talented individuals. Building it together with the community ensures that we build a product that solves real problems for real people. We'd love to have your help too: Please feel free to open issues or provide pull requests.

Check out the CONTRIBUTING.md file for contribution guidelines and the Unleash developer guide for tips on environment setup, running the tests, and running Unleash from source.

Contributors

The Unleash contributors


Features our users love

Flexibility and adaptability

Security and performance

  • Privacy by design (GDPR and Schrems II). End-user data never leaves your application.
  • Audit logs
  • Enforce OWASP's secure headers via the strict HTTPS-only mode
  • Flexible hosting options: host it on premise or in the cloud (any cloud)
  • Scale the Unleash Proxy independently of the Unleash server to support any number of front-end clients without overloading your Unleash instance

Looking for more features?

If you're looking for one of the following features, please take a look at our Pro and Enterprise plans:


Architecture

Read more in the system overview section of the Unleash documentation.


Unleash SDKs

To connect your application to Unleash you'll need to use a client SDK for your programming language.

Official server-side SDKs:

Official front-end SDKs:

The front-end SDKs connects via the Unleash Proxy in order to ensure privacy, scalability and security.

Community SDKs:

If none of the official SDKs fit your need, there's also a number of community-developed SDKs where you might find an implementation for your preferred language (such as Elixir, Dart, Clojure, and more).


Users of Unleash

Unleash is trusted by thousands of companies all over the world.

Proud Open-Source users: (send us a message if you want to add your logo here)

The Unleash logo encircled by logos for Finn.no, nav (the Norwegian Labour and Welfare Administration), Budgets, Otovo, and Amedia. The encircling logos are all connected to the Unleash logo.


Migration guides

Unleash has evolved significantly over the past few years, and we know how hard it can be to keep software up to date. If you're using the current major version, upgrading shouldn't be an issue. If you're on a previous major version, check out the Unleash migration guide!


Want to know more about Unleash?

Videos and podcasts

Articles and more

unleash-client-dotnet's People

Contributors

amj1985 avatar andreas-unleash avatar andreaschristianson avatar bremnes avatar daveleek avatar ddunkin avatar dependabot[bot] avatar gardleopard avatar gnab avatar ivarconr avatar jonms90 avatar jtone123 avatar kwasniew avatar lounsbrough avatar luanraithz avatar natevonbenken avatar nathanmascitelli avatar nikolajdl avatar nunogois avatar olehfitsyk avatar sighphyre avatar silviogreuel avatar sjaanus avatar stiano avatar thomasheartman avatar thompson-tomo avatar tymek avatar wesleimp avatar yayburritos avatar zapplebee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

unleash-client-dotnet's Issues

feat: FlexibleRollout should support "custom" stickiness.

The Flexible Rollout strategy supports multiple options for stickiness, used to define how we guarantee consistency for a gradual rollout.

Today it support the following options:

  • default (Unleash chooses the first value present on the context in defined order userId, sessionId, random.)
  • userId
  • sessionId
  • random

We have now extended the protocol to support any field in the unleash context. In addition It can be any of the predefined context field, in addition to any custom properties the user has defined.

This means that the "stickiness" parameter in the strategy configuration can now be any field on the context, and the SDK should use the specified context field to calculate the "hash" which determine whether the activation strategy should evaluate to true or false.

How it looks in the UI:
image

Edge case:

  • If the specified context field is not specific the activation strategy should evaluate to false.

To guide the implementation we have:

  • added new client-specifications, see pr-11
  • implemented PoC in Node SDK see pr-201

Allow for Unleash to be used with Microsoft.FeatureManagement

Summary of pain point
I can not easily sync my feature flags to my on-premise software without implementing a local api for software to call. I already have a secured solution to sync the feature flags to local servers and don't want local solution to talk directly to unleash.

The solution I'd like
unleash client for dotnet includes the following:

  • A dotnet configuration provider for fetching feature flags from unleash including configurable refresh interval
  • All of the unleash policies are available as implementations of IFeatureFilter in Microsoft SDK
  • the Microsoft implementation of checking if a feature is enabled is used which provides the ability to enable controllers/action based on the state.
  • views can also directly have feature checking as per Microsoft implementation

Describe alternatives you've considered
Rolling out my own api which behaves like unleash

Additional context
Changed to Usage metric capture/transfer is not in scope. Perhaps another issue be created, to look at how standard usage metric export could be utilized.

Timing bug in SystemTimerScheduledTaskManager

There is a timing bug in SystemTimerScheduledTaskManager that can prevent the Unleash client from properly scheduling the fetch-feature-toggle-task. This means that the Unleash client will silently not fetch feature toggles.

The problem is the gap in time between when a background task's Timer is created and the Timer is registered in the timers dictionary.

Specifically: the problem occurs if the callback associated with a task's Timer executes and reaches this line before the Timer is put in the dictionary:
https://github.com/Unleash/unleash-client-dotnet/blob/master/src/Unleash/Scheduling/SystemTimerScheduledTaskManager.cs#L33

Then the callback terminates early, and does not go on to reschedule itself by changing the dueTime for the Timer. Since the period for the Timer is set to infinite, it means that the Timer will never fire again. In other words, the callback executes once and terminates early, without running the task, and without making sure that the Timer will fire again.

Add Code of Conduct

Create a document that establishes expectations for behavior for your project’s participants. Adopting, and enforcing, a code of conduct can help create a positive social atmosphere for your community. Transparency should be primordial.

Allow configurable retry strategies for fetching feature toggles

Is your feature request related to a problem? Please describe.
I'd like a way to set a retry strategy for fetching feature toggles. Currently if the Unleash server is unreachable, or if there is some wrong configuration, the fetch feature toggle task will throw errors every 30 seconds (by default).

Describe the solution you'd like
A way to configure a retry strategy for fetching feature toggles, or being able to pause and start the task. Example of strategies: "If fetching toggles fails five times in a row, pause for one hour" or "If fetching feature toggles fail, wait with exponential or incremental backoff up to 30 minutes until succeeding"

Describe alternatives you've considered
One alternative has been to use synchronous startup, however I don't want the application to fail to launch if something is wrong with unleash at the moment, just to behave as if the client returned the default value every time I call IsEnabled().

Another alternative is to create my own implementation of IUnleash or skip using the SDK, however this feels like recreating the wheel when the other features Ineed are already implemented.

The alternative I currently use is to ping the server during startup before registering the DefaultUnleash for dependency injection. If I get a 200 OK I continue as normal, but if I don't get a response I set the intervals for fetching toggles and sending metrics to TimeSpan.Zero. This allows me to use the IUnleash as normal in the implementations, but the only way to resume fetching toggles is to restart the service.

Additional context
I know this is a bit of a edge case, as once the Unleash environment is properly configured for all environments this most likely won't be a frequent scenario, but during the implementation phase I'd like to push early implementations without everything being ready just yet.

Allow custom http headers to be rotated during the life of the client

Is your feature request related to a problem? Please describe.
We would like to use tokens to prevent unauthorized access to unleash server api endpoints, /api/client/features for example. We can add a custom http header in the settings when the client is started, but we have no way to rotate the token when it is refreshed.

Describe the solution you'd like
A solution we'd be interested in pursuing is to provide a new httpHeadersProvider setting at client startup that could be called to provide headers on a per request basis:

public class CustomHttpHeaderProvider : IUnleashCustomHttpHeaderProvider
{
    public getCustomHeaders Dictionary<string, string>
    {
        get {
            // get cached, new or refreshed token as needed
            return new Dictionary<string, string>()
            {
                { "Authorization", $"Bearer {token}" }
            };
        }
    }
}

var settings = new UnleashSettings()
{
    AppName = "dotnet-test",
    InstanceTag = "instance z",
    UnleashApi = new Uri("http://unleash.herokuapp.com/api/"),
    UnleashContextProvider = new AspNetContextProvider(),
    UnleashCustomHttpHeaderProvider = new CustomHttpHeaderProvider()
};

This new setting could then be consumed in https://github.com/Unleash/unleash-client-core/blob/master/src/Unleash/Communication/UnleashApiClient.cs

Describe alternatives you've considered
Without client changes, this could be overcome in the following ways:

  1. Use a local proxy to add the needed headers to each unleash request
    • Con: some setup required in each client app
  2. Keep a reference to the dictionary provided to the UnleashSettings CustomHttpHeaders field and update the dictionary periodically
    • Con: kludgy and not event oriented
  3. Do not impose a security policy on client endpoints on unleash server
    • This is our current solution as we poc unleash
    • Con: We would like to lock down even GET access to unleash

Errors sending metrics are very hard to diagnose

Describe the bug
I'm running a web application that receives feature toggles very well but it wasn't sending any metrics in production.
The cause was that the AppName was set to an empty string in the production config but not in the local environment. The API was returning a Bad Request response.
There was no indication as to what the cause was and it was very difficult to diagnose as the production servers are not accessible. There were no logs and we couldn't find any way to get diagnostic information out of the client.
We ended up creating our own implementation of the metrics client just to catch and log the error.

To Reproduce
Steps to reproduce the behavior:

  1. Configure the client without an AppName
  2. Set the metrics interval

Expected behavior
Some indication or warning that the metrics are not being sent correctly and why, and/or ability to plug in our own logger.

Additional context
I believe this is more a bug than a feature request as I really was expecting some feedback from the client regarding the misconfiguration.

feat: add support static context fields

In order to prepare the SDKS for strategy constraints we need to introduce two new fields in the Unleash Context

  • environment (default value is "default")
  • appName (already a required config parameter)

Both these static context fields should be configured at initialisation of the unleash SDK and be set as default values of the Unleash Context provided to the strategy implementations. The user is allowed to override these values in the context object at runtime (this is required to be backward compatible).

This support should be possible to add without breaking any contract and should be released as a minor version (3.3.x).

Add override option for providing context when checking a feature toggle

Is your feature request related to a problem? Please describe.
The programming pattern of using the global HttpContext is not a recommended pattern, nor is it natively supported by dotnet core.

A simple way to bypass this issue would be to pass in context with every request to check if a flag is enabled, but this is not available.

Describe the solution you'd like
Adding a simple override for passing in the context on IsEnabled

public bool IsEnabled(string toggleName, UnleashContext context, bool defaultSetting)
        {
            return CheckIsEnabled(toggleName, context, defaultSetting);
        }

Describe alternatives you've considered

  1. Force the HttpContext to be available, but it goes against the reason why it was removed in the first place
    https://stackoverflow.com/questions/38571032/how-to-get-httpcontext-current-in-asp-net-core
  2. Make the CheckIsEnabled function public. This would work, but it is a naming inconsistency with the IsEnabled

Additional context
I will have authentication information in my controller of the request, but I currently can't access the HttpContext without some workaround. I was trying to figure out exactly what my ContextProvider will look like and since we don't have things like local cache or session information, but we have the userid at the time of the request, this seems like the best way that I can think of.

If there is a better way to do this with a dotnetcore2.0 application, I would love to know.

Socket leak in HttpClientFactory

Description

DefaultHttpClientFactory is opening a new socket for each request and its throwing a socket exception in high availability API

Cause

Check that DefaultHttpClientFactory is creating a new instance of httpclient for each request.

Expected behavior

HttpClient is intended to be instantiated once and re-used throughout the life of an application.

Screenshots

Default implementation

image_socket_leak

Single instance per dns implementation

image_singleton_implementation

Default strategies not available when supplying custom strategies

I would have expected the custom strategies to be added to the default ones. Unfortunately the default ones are skipped.

If this is a deliberate design decision, please consider exposing a public property to access the default strategies, so one can choose to pass these in as part of the custom strategies.

Consider replacing LibLog with Microsoft.Extensions.Logging.Abstractions

Consider replacing the LibLog dependency with Microsoft's logging abstractions. This would allow library consumers to plug this client in to their application and have the library inherit the application-configured logging providers.

This would require dropping framework support < netstandard2.0 but it's still adaptable in a framework-era ASP.NET code.

Implement Global Segments

Is your feature request related to a problem? Please describe.
Just a feature that needs to be implemented - Global Segments

Describe the solution you'd like

Unleash v4.13 supports enhanced responses for global segments, it would be great if this SDK can make use of this. There's a few parts to the enhanced responses:

Background

Segments are effectively a way for Unleash users to define a list of constraints in such a way that makes them reusable across toggles without manually copying the constraint across to another toggle. Segments have two modes of operation, from the SDK's perspective, the inline mode will have no impact, segments will be remapped on the server side into constraints on the toggle information, no changes need to be . The second mode, global segments, requires that the SDK both opt in and handle the response differently. The handling should effectively result in unpacking the segments referenced in the feature strategies into a set of constraints. The changes required are described below.

Control Header

The SDK needs to pass up a Unleash-Client-Spec header with a semver value greater or equal to 4.2.0 (i.e. be greater or equal to the version of the unleash client spec tests where global segments are described) when hitting the get toggles endpoint on the Unleash server. This will enable the Unleash server to respond with the enhanced format

Example of the difference between enhanced and standard format:

Standard Format (default)

   "version": 2,
   "features": [  
       {
           "strategies": [
               {
                   "name": "flexibleRollout",
                   "constraints": [
                       {
                           "values": [
                               "31"
                           ],
                           "inverted": false,
                           "operator": "IN",
                           "contextName": "appName",
                           "caseInsensitive": false
                       }
                   ],          
                   "parameters": {
                       "groupId": "Test1",
                       "rollout": "100",
                       "stickiness": "default"
                   }
               }
           ],
           "name": "Test1"
       },
       {
           "strategies": [
               {
                   "name": "flexibleRollout",
                   "constraints": [
                       {
                           "values": [
                               "31"
                           ],
                           "inverted": false,
                           "operator": "IN",
                           "contextName": "appName",
                           "caseInsensitive": false
                       }
                   ],          
                   "parameters": {
                       "groupId": "Test2",
                       "rollout": "100",
                       "stickiness": "default"
                   }
               }
           ],
           "name": "Test2"
       }    
   ],
   "query": {
       "environment": "default"
   }
}

Enhanced Format (requires opt in)

{
   "version": 2,
   "features": [   
       {
           "strategies": [
               {
                   "name": "flexibleRollout",
                   "constraints": [],
                   "parameters": {
                       "groupId": "Test1",
                       "rollout": "100",
                       "stickiness": "default"
                   },
                   "segments": [
                       1
                   ]
               }
           ],
           "name": "Test1"
       },
       {
           "strategies": [
               {
                   "name": "flexibleRollout",
                   "constraints": [],
                   "parameters": {
                       "groupId": "Test2",
                       "rollout": "100",
                       "stickiness": "default"
                   },
                   "segments": [
                       1
                   ]
               }
           ],
           "name": "Test2"
       }     
   ],
   "query": {
       "environment": "default"
   },
   "segments": [
       {
           "id": 1,           
           "constraints": [
               {
                   "values": [
                       "31"
                   ],
                   "inverted": false,
                   "operator": "IN",
                   "contextName": "appName",
                   "caseInsensitive": false
               }
           ]           
       }
   ]
}

The relevant changes between the two formats are that in the enhanced format the segments are defined once as a global list and referenced within the strategy on the toggle by its ID. What's important to note is that the two above packets should be
handled identically, they reference the same toggle state.

Considerations

  • Global segments are intended to handle large datasets, how large has not been formally specified yet but expectations are around 1 000 to 10 000 segments across 1 000 toggles. As a result, time and space complexity of the implementations needs to be considered.
  • In the case of global segments, if the mapping from segment id to segment is incomplete i.e. a segment id is referenced in a toggle strategy that doesn’t map back to the global segments list, then the toggle should be evaluated to false. This is enforced through one of the client specification tests in v4.2.0 of the client spec
  • A reference implementation is provided in node JS: Unleash/unleash-client-node#329 (note that this doesn't include the header, that can be seen here: Unleash/unleash-client-node#335)

Add Type to feature toggle object and expose the feature toggle collection readonly publicly.

Is your feature request related to a problem? Please describe.
Feature parity with other client. Similar to how the node client allows you to get all feature flag definitions: https://github.com/Unleash/unleash-client-node#3-use-unleash
Also, the endpoint to grab features from the server "client/features" includes the "Type" field which could be useful if we have access to the entire collection.

Describe the solution you'd like
Add "Type" as a string to the FeatureFlag class.
Expose a public getter that returns whatever the most recent collection of feature flag definitions exist. This could be as simple as adding this to DefaultUnleash.cs
public ICollection<FeatureToggle> FeatureToggles { get { return services.ToggleCollection.Instance.Features; } }
Doing this would require adjusting the access modifier on some classes like FeatureToggle or would require some sort of mapping to a new public friendly entity.

Describe alternatives you've considered
The only other means of doing this would be to hit the api endpoint manually or directly hitting the database. Neither of these are great options.

Additional context
Currently, using the net client, a consumer can only grab a flag by name. Simply exposing the feature flag definition collection would allow a consumer to grab toggles on a type.

feat: Add support for bootstrapping feature toggles

Improve resilience by implementing the Bootstrap feature of Unleash. Feature writeup:
https://www.getunleash.io/blog/bootstrap-unleash-java-sdk-to-improve-the-resilience

Reference implementation in Java SDK:
Unleash/unleash-client-java#131

Primary goals:

  • Add a property to UnleashSettings for configuring an IToggleBootstrapProvider to use
  • Add an implementation of IToggleBootstrapProvider that can read from a specified file path
  • Add an implementation of IToggleBootstrapProvider that can GET from a specified url
  • Read from IToggleBootstrapProvider only when there is no backup file or the backup files ToggleCollection is empty

As a user, I would like to be able to fetch feature toggles synchronously during startup

The Java client for Unleash (https://github.com/Unleash/unleash-client-java) has a flag in UnleashSettings called synchronousFetchOnInitialisation.
Currently, even though feature toggles can be fetched on startup (and are by default), it is not done synchronously.

Expected behavior is to have feature flags loaded immediately after instantiating DefaultUnleash. Observed behavior is that feature flags are loaded in the background, and available some time later. As far as I can tell, use of feature flags on startup requires a short sleep after calling DefaultUnleash, which is unpleasant and racecondition-y.

DI scoping issue when not using statics

I'm having trouble hooking Unleash up in IServiceCollection. We're trying to use the DI IDisposable BeginScope() to store an IUnleashContextProvider backed by a Dictionary<string, string> as opposed to relying on HttpContext.Current.Items as in the README. The reason is b/c we use the scope pattern outside of ASP.NET Core and would like to be able to use the same Unleash boilerplate code regardless of whether we're in an ASP.NET Core app, a generic host builder, or a simple console app (creating a scope per ASP.NET request, MQ message received, timer interval, etc.).

This almost works, except that the IUnleashContextProvider is a property on the UnleashSettings class. IUnleash is registered as a singleton (per the docs), IUnleashContextProvider is registered as Scoped (also recommended in the docs and critical to capture request/message/etc-specific scope). UnleashSettings is registered as a singleton.

When ASPNETCORE_ENVIRONMENT=development, IServiceCollection.BuildServiceProvider(true) is called by the framework, which ensures that we're not capturing scoped/transient dependencies in a singleton. As far as I can tell it's unavoidable given the current architecture... I tried (see here: https://github.com/scottt732/UnleashClientTests/tree/master/UnleashClientTests).

DefaultUnleash(singleton) -> UnleashSettings(singleton) -> UnleashContextProvider(necessarily scoped, effectively captured) = invalid (single state would be shared across requests/race conditions).

The example code in the README ~gets around this issue by effectively having the UnleashContextProvider as a singleton which refers to HttpContext.Current?.Items. It will happen to be there in an ASP.NET request because your BeginRequest handler puts it there, but this requires using a static for state and breaks the ability for the same configuration to work in different application types.

I think a solution to this would be to decouple DefaultUnleash into 2 pieces--the thing that sets up services & manages the timers and the thing that answers IsEnabled questions. This would remove the IUnleashContextProvider from the UnleashSettings class.

// Configuration
services.AddSingleton<IStrategy, SomeStrategy>();
services.AddSingleton<UnleashSettings>(new UnleashSettings { ... });

// depends on settings & strategies, disposed at app end
services.AddSingleton<IUnleashServiceManager, UnleashServiceManager>(); 

// Request-specific
services.AddScoped<IUnleashContextProvider, DefaultUnleashContextProvider>();
services.AddScoped<IUnleash, DefaultUnleash>();

This would allow the IsEnabled calls to refer to the scoped IUnleashContextProvider

feat: Custom stickiness for variants

This is kind of similar to #71, but for variants.

The stickiness will be defined as a field on all the variants in the variants array, this to ensure we are not breaking our protocol. In the example we can see the stickiness is configured to "tenantId".

{"variants": [
     {
		"name": "yellow",
		"weight": 166,
		"weightType": "variable",
		"payload": {
			"type": "string",
			"value": "yellow submarine"
		},
		"overrides": [],
		"stickiness": "tenantId"
	}, {
		"name": "salmon",
		"weight": 166,
		"weightType": "variable",
		"payload": {
			"type": "string",
			"value": "salmon"
		},
		"overrides": [],
		"stickiness": "tenantId"
	}
]}

How it looks in Unleash Admin UI:
image

Edge cases:
If no stickiness is defined the SDK should assume "default" stickiness as today.

To guide the implementation we have:

  • added new client-specifications, see pr-11
  • implemented PoC in Node SDK see pr-202

Usage Report

Describe the bug
Unleash.Client is not reporting Usage of the Toggle like JS sdk does. Is there some parameter or config that needs to be enabled for this feature?

image

iApartments_AccessHistoryFilters toggle is accessed via Unleash.Client and all other via JS sdk.

Thanks

UnleashClientFactory and custom strategies

Not sure if this is a feature request or just something I've missed in the API but I'm struggling to register a custom strategy when creating an instance of the client via UnleashClientFactory. Both of the CreateClient... methods seem to be hard-coded with Array.Empty<IStrategy>().

My use case is that I would like to create the client synchronously with a custom strategy. Any suggestions on the best way to achieve this?

Update readme

  • should state that it is not a official client
  • should link to correct nuget package.

feat: add support for "project" query

The Unleash API client endpoint support query for specific project, to avoid having the SDK download all feature toggles like this:

http https://app.unleash-hosted.com/demo/api/client/features?project=solar 
Authorization:56907a2fa53c1d16101d509a10b78e36190b0f918d9f122d

Which will only return feature flags for projectId=solar.

This should be implemented by adding a new configuration to the unleash SDK to specify the "projectId" and of specified it should also be added to the feature-toggle query sent to Unleash API.

Make GetVariant(string toggleName, UnleashContext context, Variant defaultValue) public

Is your feature request related to a problem? Please describe.
I can pass in a UnleashContext into the IsEnabled method, but not the GetVariant. As I looked in the source code, I was able to find that there is a private method that does exactly what I want.

Describe the solution you'd like
Ideally, the method below would be public and exposed in the interface:

public Variant GetVariant(string toggleName, UnleashContext context, Variant defaultValue)
{
      var toggle = GetToggle(toggleName);
      var enabled = CheckIsEnabled(toggleName, context, false);
      var variant = enabled ? VariantUtils.SelectVariant(toggle, context, defaultValue) : defaultValue;

      RegisterVariant(toggleName, variant);
      return variant;
}

Describe alternatives you've considered
I have looked at adding extension methods, but that gets messy.

Extend IUnleash interface with additional GetVariants method implementation

Is your feature request related to a problem? Please describe.
I'm building a wrapper around the IUnleash interface to be able to use DI and scoped IUnleashContextProvider. The idea of it is quite simple. Every time someone uses the IUnleash interface and does not pass its own UnleashContext in a method, I automatically pass UnleashContext from DI. Most of the methods have a version where you can pass UnleashContext as a parameter. But there's no such option for the GetVariants method that uses IsEnabled(toggleName) under the hood and can use UnleashContext.

Describe the solution you'd like
From a consistency point of view, it makes sense to introduce IEnumerable<VariantDefinition> GetVariants(string toggleName, UnleashContext context);. It won't break existing integrations so shouldn't be considered a breaking change. If you guys approve it, I can gladly assist with PR.

Additional context
I'm trying to do something very similar to this component-wise.

Broadcast changes to clients

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Not able to get the feature toggle value from .net console application

I am trying to access unleash feature toggle from my .net-based console application but while checking for IsEnabled it always returns a false value.

public class UnleashToggle : IDisposable
	{
		public static IUnleash Unleash { get; private set; }
		public UnleashSettings UnleashSettings { get; private set; }
		private string unleashUrl;
		private string unleashAuthKey;

		public UnleashToggle()
		{

		}
		public void SetUpUnleash()
		{
			try
			{
				unleashUrl = "my url";
				unleashAuthKey = "my key";

				UnleashSettings = new UnleashSettings()
				{
					UnleashApi = new Uri(unleashUrl),
					AppName = "CloudHR",
					InstanceTag = "Tag",
					FetchTogglesInterval = TimeSpan.FromSeconds(30),
					UnleashContextProvider = new UnleashContextProvider(),
					CustomHttpHeaders = new Dictionary<string, string>()
					{
						{ "authorization", unleashAuthKey }
					}
				};

				//CustomerWithIdStrategy customerWithIdStrategy = new CustomerWithIdStrategy();
				//InstanceWithIDStrategy instanceWithIDStrategy = new InstanceWithIDStrategy();
				Unleash = new DefaultUnleash(UnleashSettings);
			}
			catch (Exception ex)
			{
				LogError("ctor", ex);
			}
		}

		private void LogError(string method, Exception ex)
		{
			string message = string.Format("Method: {0} at Time: {1}", method, DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss tt"));
			message += Environment.NewLine;
			message += "-----------------------------------------------------------";
			message += Environment.NewLine;
			message += string.Format("Message: {0}", ex.Message);
			message += Environment.NewLine;
			message += string.Format("StackTrace: {0}", ex.StackTrace);
			message += Environment.NewLine;
			message += string.Format("Source: {0}", ex.Source);
			message += Environment.NewLine;
			message += string.Format("TargetSite: {0}", ex.TargetSite.ToString());
			message += Environment.NewLine;
			message += "-----------------------------------------------------------";
			message += Environment.NewLine;

			string strFileName = "log.txt";
			string fileLoc = string.Format("{0}\\{1}", Path.GetTempPath(), strFileName);

			FileStream fs = null;
			if ((!File.Exists(fileLoc)))
			{
				fs = File.Create(fileLoc);
				fs.Close();
			}

			if (File.Exists(fileLoc))
			{
				using (StreamWriter sw = new StreamWriter(fileLoc, true))
				{
					sw.WriteLine(message);
					sw.Close();
				}
			}
		}

		public bool IsEnabled(string featureName)
		{
			try
			{
				return Unleash.IsEnabled(featureName);
			}
			catch (Exception ex)
			{
				LogError("IsEnabled", ex);
				return false;
			}
		}
		public void Dispose()
		{
			Unleash.Dispose();
		}
	}

I am using the default strategy for this.
image

Any suggestions?

File not being re-created when deleted.

Describe the bug
When the .json file or/and the etag file are removed, the SDK is not re-creating the file.

To Reproduce
Steps to reproduce the behavior:

  1. Connect to Unleash.
  2. Create UnleashClient.
  3. Request Feature Flags.
  4. Files are going to be created (.json and etag).
  5. Remove both files.
  6. The scheduler that has FetchFeatureToggleTask.cs will not re-create the file.
  7. This is because the logic under ExecuteAsync will verify first if the Feature Flags have changed. Because they didn't we are exiting the scheduler.
  8. The same scenario happen when the Etag is the same, will exit before checking if the file exists.

Expected behavior
The TEMP folder that you suggest, in most of the cases, will be deleted for any reason on the server-side. I would assume that the first thing that we want to check is if the file exists.
If the doesn't exist, we should be aiming to recreate with the FF that where fetched.
Otherwise, we do the same check of HasChanged and Etag = Etag.

Screenshots
Unleash – FetchFeatureTogglesTask cs

Desktop (please complete the following information):

  • OS: Windows 10
  • Browser Firefox
  • Version 87.0
  • We are doing this on the server-side, but I also tested with the samples from cloning the repo.

Client doesn't find features / can't connect to API if `UnleashAPI` doesn't end with `/`

Describe the bug
When provided with a settings.UnleashApi value without a trailing slash, the client seems unable to connect to the API.

To Reproduce
Steps to reproduce the behavior:

  1. Create a Web API and set up a singleton Unleash service. Use a URL to an Unleash API without the trailing /, i.e. https://<unleash>/api
  2. Check whether a feature that is enabled in the Unleash API is enabled (or can be found).
  3. See that the feature can't be found.

Expected behavior
The client should be able to connect to the Unleash API regardless of whether the provided API url ends with a / or not.

Desktop (please complete the following information):

  • OS: Linux (Docker image: mcr.microsoft.com/dotnet/core/sdk:3.1)

Additional context
I ran into this when trying to troubleshoot why the Unleash client told me that none of the toggles were enabled. Double-checking against a version I had running locally revealed that the local version had a trailing slash while the other did not.

This seems like something that is super easy to miss and could make you scratch your head for hours. I also did not see an explicit mention of this in the readme.

Apart from that, though, the client has been very easy to use and well documented, so good job and thanks!

Unhandled exception from background task 'fetch-feature-toggles-task'

Describe the bug
I'm using the GitLab feature flag service which utilizes Unleash. The clients of our Windows desktop application seem to sporadically receive a few different error messages.

To Reproduce
The following code snippet causes the error to be thrown on some of our client's machines:

            UnleashSettings settings = new UnleashSettings()
            {
                AppName = appConfig.AppName,
                Environment = appConfig.Environment,
                UnleashApi = new Uri(appConfig.GitLabUnleashApiUrl),
                InstanceTag = appConfig.GitLabUnleashInstanceId,
                CustomHttpHeaders = new Dictionary<string, string>()
                {
                    {"Authorization", $"Bearer {appConfig.GitLabFeatureFlagAccessToken}" }
                }
            };

            IUnleash unleash = new DefaultUnleash(settings);
            unleash.IsEnabled("testToggle");

Expected behavior
The unleash.IsEnabled("testToggle") call to result in a full call to the GitLab feature flag service and not throw an exception.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]: Windows 10
  • Browser [e.g. chrome, safari]: Chrome
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Full stack traces below:

UNLEASH: Unhandled exception from background task 'fetch-feature-toggles-task'.  System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The remote name could not be resolved: 'gitlab.com'     at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)     at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)     --- End of inner exception stack trace ---     at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)     at Unleash.Communication.UnleashApiClient.<FetchToggles>d__5.MoveNext()  --- End of stack trace from previous location where exception was thrown ---     at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)     at Unleash.Scheduling.FetchFeatureTogglesTask.<ExecuteAsync>d__11.MoveNext()  --- End of stack trace from previous location where exception was thrown ---     at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)     at Unleash.Scheduling.SystemTimerScheduledTaskManager.<>c__DisplayClass3_0.<<ConfigureTask>g__Callback|0>d.MoveNext() 
UNLEASH: Unhandled exception from background task 'fetch-feature-toggles-task'.  System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Authentication failed because the remote party has closed the transport stream.     at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult)     at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)
UNLEASH: Task 'fetch-feature-toggles-task' cancelled ...  System.Threading.Tasks.TaskCanceledException: A task was canceled.     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)     at Unleash.Communication.UnleashApiClient.<FetchToggles>d__5.MoveNext()  --- End of stack trace from previous location where exception was thrown ---     at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)     at Unleash.Scheduling.FetchFeatureTogglesTask.<ExecuteAsync>d__11.MoveNext()  --- End of stack trace from previous location where exception was thrown ---     at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)     at Unleash.Scheduling.SystemTimerScheduledTaskManager.<>c__DisplayClass3_0.<<ConfigureTask>g__Callback|0>d.MoveNext() 

Add support for static context fields.

In order to prepare the SDKS for strategy constraints we need to introduce two new fields in the Unleash Context

  • environment (default value is "default")
  • appName (already a required config parameter)

Both these static context fields should be configured at initialisation of the unleash SDK and be set as default values of the Unleash Context provided to the strategy implementations. The user is allowed to override these values in the context object at runtime (this is required to be backward compatible).

This support should be possible to add without breaking any contract and should be released as a minor version (3.3.x).

Consider using HttpClientFactory

If you took a dependency on Netstandard2.0 (drop support for .NET Framework <= 4.7.1 I think), you could use the official HttpClientFactory, allowing your library/end-users to handle DNS changes gracefully, pool TCP connections, and to configure retry policies.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.