Skip to content

Commit

Permalink
chore: Revert "feat: automatically generated instanceId (#226)" (#233)
Browse files Browse the repository at this point in the history
* chore: Revert "feat: automatically generated instanceId (#226)"

* style: formatting
  • Loading branch information
nunogois authored Jul 15, 2024
1 parent 7c639dd commit a153aea
Show file tree
Hide file tree
Showing 19 changed files with 123 additions and 19 deletions.
22 changes: 17 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ End Class

Dim unleashSettings As New UnleashSettings()
unleashSettings.AppName = "dotnet-test"
unleashSettings.InstanceTag = "instance z"
' add the custom http header provider to the settings
unleashSettings.UnleashCustomHttpHeaderProvider = New CustomHttpHeaderProvider()
unleashSettings.UnleashApi = new Uri("http://unleash.herokuapp.com/api/")
Expand Down Expand Up @@ -424,13 +425,24 @@ By default unleash-client fetches the feature toggles from unleash-server every
* When .json file does not exists
* When the named feature toggle does not exist in .json file

The backup file name will follow this pattern: `{fileNameWithoutExtension}-{AppName}-{SdkVersion}.{extension}`.
The backup file name will follow this pattern: `{fileNameWithoutExtension}-{AppName}-{InstanceTag}-{SdkVersion}.{extension}`, where InstanceTag is either what you configure on `UnleashSettings` during startup, or a formatted string with a random component following this pattern: `{Dns.GetHostName()}-generated-{Guid.NewGuid()}`.

### InstanceTag
You can configure InstanceTag like this:

As of version `5.0.0`, `InstanceTag` is no longer a property of `UnleashSettings`. Instead, an internal `InstanceId` is automatically generated.

This means that the backup file names have been simplified and no longer include the `InstanceTag` property value.
```csharp
var settings = new UnleashSettings()
{
AppName = "dotnet-test",
UnleashApi = new Uri("http://unleash.herokuapp.com/api/"),
// Set an instance tag for consistent backup file naming
InstanceTag = "CustomInstanceTag",
UnleashContextProvider = new AspNetContextProvider(),
CustomHttpHeaders = new Dictionary<string, string>()
{
{"Authorization", "API token" }
}
};
```

## Bootstrapping
* Unleash supports bootstrapping from a JSON string.
Expand Down
1 change: 1 addition & 0 deletions samples/WebApp/Global.asax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ protected void Application_Start()
UnleashApi = new Uri("http://unleash.herokuapp.com/api/"),
//UnleashApi = new Uri("http://localhost:4242/api/"),
AppName = "dotnet-api-test",
InstanceTag = "instance 1",
SendMetricsInterval = TimeSpan.FromSeconds(20),
UnleashContextProvider = new AspNetContextProvider(),
//JsonSerializer = new JsonNetSerializer()
Expand Down
1 change: 1 addition & 0 deletions samples/WebApplication/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public void ConfigureServices(IServiceCollection services)
{
UnleashApi = new Uri("http://localhost:4242/api"),
AppName = "variant-sample",
InstanceTag = "instance 1",
SendMetricsInterval = TimeSpan.FromSeconds(10),
FetchTogglesInterval = TimeSpan.FromSeconds(10),
});
Expand Down
1 change: 1 addition & 0 deletions samples/WinFormsApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ static void Main()
UnleashApi = new Uri("http://unleash.herokuapp.com/api/"),
//UnleashApi = new Uri("http://localhost:4242/api/"),
AppName = "dotnet-forms-test",
InstanceTag = "instance 1",
SendMetricsInterval = TimeSpan.FromSeconds(5),
FetchTogglesInterval = TimeSpan.FromSeconds(10),
UnleashContextProvider = new WinFormsContextProvider(form),
Expand Down
4 changes: 2 additions & 2 deletions src/Unleash/Communication/UnleashApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public async Task<bool> SendMetrics(ThreadSafeMetricsBucket metrics, Cancellatio
jsonSerializer.Serialize(memoryStream, new ClientMetrics
{
AppName = clientRequestHeaders.AppName,
InstanceId = clientRequestHeaders.InstanceId,
InstanceId = clientRequestHeaders.InstanceTag,
Bucket = bucket
});
}
Expand Down Expand Up @@ -290,7 +290,7 @@ private static void SetRequestHeaders(HttpRequestMessage requestMessage, Unleash

requestMessage.Headers.TryAddWithoutValidation(appNameHeader, headers.AppName);
requestMessage.Headers.TryAddWithoutValidation(userAgentHeader, headers.AppName);
requestMessage.Headers.TryAddWithoutValidation(instanceIdHeader, headers.InstanceId);
requestMessage.Headers.TryAddWithoutValidation(instanceIdHeader, headers.InstanceTag);
requestMessage.Headers.TryAddWithoutValidation(supportedSpecVersionHeader, headers.SupportedSpecVersion);

SetCustomHeaders(requestMessage, headers.CustomHttpHeaders);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Unleash.Communication
internal class UnleashApiClientRequestHeaders
{
public string AppName { get; set; }
public string InstanceId { get; set; }
public string InstanceTag { get; set; }
public Dictionary<string, string> CustomHttpHeaders { get; set; }
public IUnleashCustomHttpHeaderProvider CustomHttpHeaderProvider { get; set; }
public string SupportedSpecVersion { get; internal set; }
Expand Down
7 changes: 6 additions & 1 deletion src/Unleash/Events/ErrorType.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
namespace Unleash.Events
using System;
using System.Collections.Generic;
using System.Text;

namespace Unleash.Events
{
public enum ErrorType
{
Client,
TogglesBackup,
Bootstrap,
ImpressionEvent,
FileCache
Expand Down
2 changes: 1 addition & 1 deletion src/Unleash/Internal/UnleashServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public UnleashServices(UnleashSettings settings, EventCallbackConfig eventConfig
apiClient = new UnleashApiClient(httpClient, settings.JsonSerializer, new UnleashApiClientRequestHeaders()
{
AppName = settings.AppName,
InstanceId = settings.InstanceId,
InstanceTag = settings.InstanceTag,
CustomHttpHeaders = settings.CustomHttpHeaders,
CustomHttpHeaderProvider = settings.UnleashCustomHttpHeaderProvider,
SupportedSpecVersion = supportedSpecVersion
Expand Down
3 changes: 3 additions & 0 deletions src/Unleash/Internal/UnleashSettingsValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public void Validate(UnleashSettings settings)
if (settings.AppName == null)
throw new UnleashException("You are required to specify an appName");

if (settings.InstanceTag == null)
throw new UnleashException("You are required to specify an instance id");

if (settings.JsonSerializer == null)
throw new UnleashException("You are required to specify an json serializer");

Expand Down
2 changes: 1 addition & 1 deletion src/Unleash/Scheduling/ClientRegistrationBackgroundTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public async Task ExecuteAsync(CancellationToken cancellationToken)
var clientRegistration = new ClientRegistration
{
AppName = settings.AppName,
InstanceId = settings.InstanceId,
InstanceId = settings.InstanceTag,
Interval = (long)settings.SendMetricsInterval.Value.TotalMilliseconds,
SdkVersion = settings.SdkVersion,
Started = DateTimeOffset.UtcNow,
Expand Down
2 changes: 2 additions & 0 deletions src/Unleash/Scheduling/FetchFeatureTogglesTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public async Task ExecuteAsync(CancellationToken cancellationToken)
catch (IOException ex)
{
Logger.Warn(() => $"UNLEASH: Exception when writing to toggle file '{toggleFile}'.", ex);
eventConfig?.RaiseError(new ErrorEvent() { ErrorType = ErrorType.TogglesBackup, Error = ex });
}

Etag = result.Etag;
Expand All @@ -100,6 +101,7 @@ public async Task ExecuteAsync(CancellationToken cancellationToken)
catch (IOException ex)
{
Logger.Warn(() => $"UNLEASH: Exception when writing to ETag file '{etagFile}'.", ex);
eventConfig?.RaiseError(new ErrorEvent() { ErrorType = ErrorType.TogglesBackup, Error = ex });
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/Unleash/UnleashSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ public class UnleashSettings
public string Environment { get; set; } = "default";

/// <summary>
/// INTERNAL: Gets or sets an instance id. Used for communication with backend api.
/// Gets or sets an instance tag. Used for communication with backend api.
/// </summary>
internal string InstanceId { get; set; } = Guid.NewGuid().ToString();
public string InstanceTag { get; set; } = GetDefaultInstanceTag();

/// <summary>
/// Sets the project to fetch feature toggles for.
Expand Down Expand Up @@ -155,6 +155,13 @@ private static string GetSdkVersion()
return $"unleash-client-dotnet:v{version}";
}

private static string GetDefaultInstanceTag()
{
var hostName = Dns.GetHostName();

return $"{hostName}-generated-{Guid.NewGuid()}";
}

/// <summary>
/// Returns info about the unleash setup.
/// </summary>
Expand All @@ -164,7 +171,7 @@ public override string ToString()

sb.AppendLine($"Application name: {AppName}");
sb.AppendLine($"Environment: {Environment}");
sb.AppendLine($"Instance Id: {InstanceId}");
sb.AppendLine($"Instance tag: {InstanceTag}");
sb.AppendLine($"Project Id: {ProjectId}");
sb.AppendLine($"Server Uri: {UnleashApi}");
sb.AppendLine($"Sdk version: {SdkVersion}");
Expand Down Expand Up @@ -208,7 +215,7 @@ private string PrependFileName(string filename)
var extension = Path.GetExtension(filename);
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filename);

return new string($"{fileNameWithoutExtension}-{AppName}-{SdkVersion}{extension}"
return new string($"{fileNameWithoutExtension}-{AppName}-{InstanceTag}-{SdkVersion}{extension}"
.Where(c => !invalidFileNameChars.Contains(c))
.ToArray());
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Unleash.Tests/ClientFactory/SyncStartupUnitTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class SyncStartupUnitTest
public void Setup()
{
mockApiClient = A.Fake<IUnleashApiClient>();
settings = new MockedUnleashSettings();
settings = new MockedUnleashSettings(instanceTag: "test instance SyncStartupUnitTest");
unleashFactory = new UnleashClientFactory();
}

Expand Down
1 change: 1 addition & 0 deletions tests/Unleash.Tests/Communication/CustomHeadersUnitTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ private IUnleashApiClient CreateApiClient()
var requestHeaders = new UnleashApiClientRequestHeaders
{
AppName = "api-test-client",
InstanceTag = "instance1",
CustomHttpHeaders = httpHeaders,
CustomHttpHeaderProvider = httpHeadersProvider
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ private UnleashApiClient NewTestableClient(string project, MockHttpMessageHandle
var requestHeaders = new UnleashApiClientRequestHeaders
{
AppName = "api-test-client",
InstanceTag = "instance1",
CustomHttpHeaders = null,
CustomHttpHeaderProvider = null
};
Expand Down
2 changes: 1 addition & 1 deletion tests/Unleash.Tests/ExampleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class ExampleTests
public async Task Setup()
{
var factory = new UnleashClientFactory();
unleash = await factory.CreateClientAsync(new MockedUnleashSettings(), true);
unleash = await factory.CreateClientAsync(new MockedUnleashSettings(instanceTag: "test instance ExampleTests"), true);
}

[Test]
Expand Down
2 changes: 1 addition & 1 deletion tests/Unleash.Tests/IOTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ private static void LockFile(object data)
[Test]
public async Task GracefullyFailsWhenFileLocked()
{
var settings = new MockedUnleashSettings(false);
var settings = new MockedUnleashSettings(false, "test instance IOTests");

var toggleFile = settings.GetFeatureToggleFilePath();
var eTagFile = settings.GetFeatureToggleETagFilePath();
Expand Down
69 changes: 69 additions & 0 deletions tests/Unleash.Tests/Internal/ErrorEvents_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,75 @@ public void FetchFeatureToggleTask_HttpRequestException_Raises_ErrorEvent()
thrownException.Should().NotBeNull();
}

[Test]
public void FetchFeatureToggleTask_Serialization_Throws_Raises_ErrorEvent()
{
// Arrange
ErrorEvent callbackEvent = null;
var exceptionMessage = "Serialization failed";
var callbackConfig = new EventCallbackConfig()
{
ErrorEvent = evt => { callbackEvent = evt; }
};

var fakeApiClient = A.Fake<IUnleashApiClient>();
A.CallTo(() => fakeApiClient.FetchToggles(A<string>._, A<CancellationToken>._, false))
.Returns(Task.FromResult(new FetchTogglesResult() { HasChanged = true, ToggleCollection = new ToggleCollection(), Etag = "one" }));

var collection = new ThreadSafeToggleCollection();
var serializer = A.Fake<IJsonSerializer>();
A.CallTo(() => serializer.Serialize(A<Stream>._, A<ToggleCollection>._))
.Throws(() => new IOException(exceptionMessage));

var filesystem = new MockFileSystem();
var tokenSource = new CancellationTokenSource();
var task = new FetchFeatureTogglesTask(fakeApiClient, collection, serializer, filesystem, callbackConfig, "togglefile.txt", "etagfile.txt", false);

// Act
Task.WaitAll(task.ExecuteAsync(tokenSource.Token));

// Assert
callbackEvent.Should().NotBeNull();
callbackEvent.Error.Should().NotBeNull();
callbackEvent.Error.Message.Should().Be(exceptionMessage);
callbackEvent.ErrorType.Should().Be(ErrorType.TogglesBackup);
}

[Test]
public void FetchFeatureToggleTask_Etag_Writing_Throws_Raises_ErrorEvent()
{
// Arrange
ErrorEvent callbackEvent = null;
var exceptionMessage = "Writing failed";
var callbackConfig = new EventCallbackConfig()
{
ErrorEvent = evt => { callbackEvent = evt; }
};

var fakeApiClient = A.Fake<IUnleashApiClient>();
A.CallTo(() => fakeApiClient.FetchToggles(A<string>._, A<CancellationToken>._, false))
.Returns(Task.FromResult(new FetchTogglesResult() { HasChanged = true, ToggleCollection = new ToggleCollection(), Etag = "one" }));

var collection = new ThreadSafeToggleCollection();
var serializer = A.Fake<IJsonSerializer>();

var filesystem = A.Fake<IFileSystem>();
A.CallTo(() => filesystem.WriteAllText(A<string>._, A<string>._))
.Throws(() => new IOException(exceptionMessage));

var tokenSource = new CancellationTokenSource();
var task = new FetchFeatureTogglesTask(fakeApiClient, collection, serializer, filesystem, callbackConfig, "togglefile.txt", "etagfile.txt", false);

// Act
Task.WaitAll(task.ExecuteAsync(tokenSource.Token));

// Assert
callbackEvent.Should().NotBeNull();
callbackEvent.Error.Should().NotBeNull();
callbackEvent.Error.Message.Should().Be(exceptionMessage);
callbackEvent.ErrorType.Should().Be(ErrorType.TogglesBackup);
}

[Test]
public void CachedFilesLoader_Raises_ErrorEvent()
{
Expand Down
3 changes: 2 additions & 1 deletion tests/Unleash.Tests/MockedUnleashSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ namespace Unleash.Tests
{
public class MockedUnleashSettings : UnleashSettings
{
public MockedUnleashSettings(bool mockFileSystem = true)
public MockedUnleashSettings(bool mockFileSystem = true, string instanceTag = "test instance 1")
{
AppName = "test";
InstanceTag = instanceTag;
UnleashApi = new Uri("http://localhost:4242/");
DisableSingletonWarning = true;

Expand Down

0 comments on commit a153aea

Please sign in to comment.