Skip to content

Commit aefd2e8

Browse files
askptarttonoyan
authored andcommitted
chore: cleanup code (open-feature#277)
<!-- Please use this template for your pull request. --> <!-- Please use the sections that you need and delete other sections --> ## This PR This PR fixes some of the warnings and typos that I recently found. More interestingly, it addresses these issues: - Missing the `.this` - Usage of `ILogger` vs `Source generator log` - `const` vs `static` - Fix nullability for some methods and properties. And a few more changes. ### Follow-up Tasks We need to do more cleanup tasks. ### How to test All of these changes are recommended by the IDE and "tested" by the compiler when it executes. --------- Signed-off-by: André Silva <[email protected]> Signed-off-by: Artyom Tonoyan <[email protected]>
1 parent 8020219 commit aefd2e8

17 files changed

+125
-138
lines changed

src/OpenFeature/Api.cs

+9-10
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public sealed class Api : IEventBus
3232
public static Api Instance { get; } = new Api();
3333

3434
// Explicit static constructor to tell C# compiler
35-
// not to mark type as beforefieldinit
35+
// not to mark type as beforeFieldInit
3636
// IE Lazy way of ensuring this is thread safe without using locks
3737
static Api() { }
3838
private Api() { }
@@ -46,7 +46,7 @@ private Api() { }
4646
public async Task SetProviderAsync(FeatureProvider featureProvider)
4747
{
4848
this._eventExecutor.RegisterDefaultFeatureProvider(featureProvider);
49-
await this._repository.SetProviderAsync(featureProvider, this.GetContext(), AfterInitialization, AfterError).ConfigureAwait(false);
49+
await this._repository.SetProviderAsync(featureProvider, this.GetContext(), this.AfterInitialization, this.AfterError).ConfigureAwait(false);
5050
}
5151

5252
/// <summary>
@@ -62,7 +62,7 @@ public async Task SetProviderAsync(string clientName, FeatureProvider featurePro
6262
throw new ArgumentNullException(nameof(clientName));
6363
}
6464
this._eventExecutor.RegisterClientFeatureProvider(clientName, featureProvider);
65-
await this._repository.SetProviderAsync(clientName, featureProvider, this.GetContext(), AfterInitialization, AfterError).ConfigureAwait(false);
65+
await this._repository.SetProviderAsync(clientName, featureProvider, this.GetContext(), this.AfterInitialization, this.AfterError).ConfigureAwait(false);
6666
}
6767

6868
/// <summary>
@@ -101,15 +101,15 @@ public FeatureProvider GetProvider(string clientName)
101101
/// </para>
102102
/// </summary>
103103
/// <returns><see cref="ClientMetadata"/></returns>
104-
public Metadata GetProviderMetadata() => this.GetProvider().GetMetadata();
104+
public Metadata? GetProviderMetadata() => this.GetProvider().GetMetadata();
105105

106106
/// <summary>
107107
/// Gets providers metadata assigned to the given clientName. If the clientName has no provider
108108
/// assigned to it the default provider will be returned
109109
/// </summary>
110110
/// <param name="clientName">Name of client</param>
111111
/// <returns>Metadata assigned to provider</returns>
112-
public Metadata GetProviderMetadata(string clientName) => this.GetProvider(clientName).GetMetadata();
112+
public Metadata? GetProviderMetadata(string clientName) => this.GetProvider(clientName).GetMetadata();
113113

114114
/// <summary>
115115
/// Create a new instance of <see cref="FeatureClient"/> using the current provider
@@ -121,7 +121,7 @@ public FeatureProvider GetProvider(string clientName)
121121
/// <returns><see cref="FeatureClient"/></returns>
122122
public FeatureClient GetClient(string? name = null, string? version = null, ILogger? logger = null,
123123
EvaluationContext? context = null) =>
124-
new FeatureClient(() => _repository.GetProvider(name), name, version, logger, context);
124+
new FeatureClient(() => this._repository.GetProvider(name), name, version, logger, context);
125125

126126
/// <summary>
127127
/// Appends list of hooks to global hooks list
@@ -277,7 +277,7 @@ private async Task AfterInitialization(FeatureProvider provider)
277277
{
278278
Type = ProviderEventTypes.ProviderReady,
279279
Message = "Provider initialization complete",
280-
ProviderName = provider.GetMetadata().Name,
280+
ProviderName = provider.GetMetadata()?.Name,
281281
};
282282

283283
await this._eventExecutor.EventChannel.Writer.WriteAsync(new Event { Provider = provider, EventPayload = eventPayload }).ConfigureAwait(false);
@@ -286,10 +286,9 @@ private async Task AfterInitialization(FeatureProvider provider)
286286
/// <summary>
287287
/// Update the provider state to ERROR and emit an ERROR after failed init.
288288
/// </summary>
289-
private async Task AfterError(FeatureProvider provider, Exception ex)
290-
289+
private async Task AfterError(FeatureProvider provider, Exception? ex)
291290
{
292-
provider.Status = typeof(ProviderFatalException) == ex.GetType() ? ProviderStatus.Fatal : ProviderStatus.Error;
291+
provider.Status = typeof(ProviderFatalException) == ex?.GetType() ? ProviderStatus.Fatal : ProviderStatus.Error;
293292
var eventPayload = new ProviderEventPayload
294293
{
295294
Type = ProviderEventTypes.ProviderError,

src/OpenFeature/Constant/Reason.cs

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,42 +9,42 @@ public static class Reason
99
/// <summary>
1010
/// Use when the flag is matched based on the evaluation context user data
1111
/// </summary>
12-
public static string TargetingMatch = "TARGETING_MATCH";
12+
public const string TargetingMatch = "TARGETING_MATCH";
1313

1414
/// <summary>
1515
/// Use when the flag is matched based on a split rule in the feature flag provider
1616
/// </summary>
17-
public static string Split = "SPLIT";
17+
public const string Split = "SPLIT";
1818

1919
/// <summary>
2020
/// Use when the flag is disabled in the feature flag provider
2121
/// </summary>
22-
public static string Disabled = "DISABLED";
22+
public const string Disabled = "DISABLED";
2323

2424
/// <summary>
2525
/// Default reason when evaluating flag
2626
/// </summary>
27-
public static string Default = "DEFAULT";
27+
public const string Default = "DEFAULT";
2828

2929
/// <summary>
3030
/// The resolved value is static (no dynamic evaluation)
3131
/// </summary>
32-
public static string Static = "STATIC";
32+
public const string Static = "STATIC";
3333

3434
/// <summary>
3535
/// The resolved value was retrieved from cache
3636
/// </summary>
37-
public static string Cached = "CACHED";
37+
public const string Cached = "CACHED";
3838

3939
/// <summary>
4040
/// Use when an unknown reason is encountered when evaluating flag.
4141
/// An example of this is if the feature provider returns a reason that is not defined in the spec
4242
/// </summary>
43-
public static string Unknown = "UNKNOWN";
43+
public const string Unknown = "UNKNOWN";
4444

4545
/// <summary>
4646
/// Use this flag when abnormal execution is encountered.
4747
/// </summary>
48-
public static string Error = "ERROR";
48+
public const string Error = "ERROR";
4949
}
5050
}

src/OpenFeature/Error/ProviderFatalException.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace OpenFeature.Error
66
{
7-
/// <summary> the
7+
/// <summary>
88
/// An exception that signals the provider has entered an irrecoverable error state.
99
/// </summary>
1010
[ExcludeFromCodeCoverage]

src/OpenFeature/EventExecutor.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ private void EmitOnRegistration(FeatureProvider? provider, ProviderEventTypes ev
206206
{
207207
handler.Invoke(new ProviderEventPayload
208208
{
209-
ProviderName = provider.GetMetadata().Name,
209+
ProviderName = provider.GetMetadata()?.Name,
210210
Type = eventType,
211211
Message = message
212212
});
@@ -322,6 +322,7 @@ private void UpdateProviderStatus(FeatureProvider provider, ProviderEventPayload
322322
case ProviderEventTypes.ProviderError:
323323
provider.Status = eventPayload.ErrorType == ErrorType.ProviderFatal ? ProviderStatus.Fatal : ProviderStatus.Error;
324324
break;
325+
case ProviderEventTypes.ProviderConfigurationChanged:
325326
default: break;
326327
}
327328
}

src/OpenFeature/FeatureProvider.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ namespace OpenFeature
1111
{
1212
/// <summary>
1313
/// The provider interface describes the abstraction layer for a feature flag provider.
14-
/// A provider acts as the translates layer between the generic feature flag structure to a target feature flag system.
14+
/// A provider acts as it translates layer between the generic feature flag structure to a target feature flag system.
1515
/// </summary>
1616
/// <seealso href="https://github.com/open-feature/spec/blob/v0.5.2/specification/sections/02-providers.md">Provider specification</seealso>
1717
public abstract class FeatureProvider
1818
{
1919
/// <summary>
20-
/// Gets a immutable list of hooks that belong to the provider.
21-
/// By default return a empty list
20+
/// Gets an immutable list of hooks that belong to the provider.
21+
/// By default, return an empty list
2222
///
2323
/// Executed in the order of hooks
2424
/// before: API, Client, Invocation, Provider
@@ -38,7 +38,7 @@ public abstract class FeatureProvider
3838
/// Metadata describing the provider.
3939
/// </summary>
4040
/// <returns><see cref="Metadata"/></returns>
41-
public abstract Metadata GetMetadata();
41+
public abstract Metadata? GetMetadata();
4242

4343
/// <summary>
4444
/// Resolves a boolean feature flag

src/OpenFeature/Model/EvaluationContextBuilder.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ public EvaluationContextBuilder Merge(EvaluationContext context)
140140
{
141141
string? newTargetingKey = "";
142142

143-
if (!string.IsNullOrWhiteSpace(TargetingKey))
143+
if (!string.IsNullOrWhiteSpace(this.TargetingKey))
144144
{
145-
newTargetingKey = TargetingKey;
145+
newTargetingKey = this.TargetingKey;
146146
}
147147

148148
if (!string.IsNullOrWhiteSpace(context.TargetingKey))

src/OpenFeature/Model/FlagEvaluationOptions.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ namespace OpenFeature.Model
1010
public sealed class FlagEvaluationOptions
1111
{
1212
/// <summary>
13-
/// A immutable list of <see cref="Hook"/>
13+
/// An immutable list of <see cref="Hook"/>
1414
/// </summary>
1515
public IImmutableList<Hook> Hooks { get; }
1616

1717
/// <summary>
18-
/// A immutable dictionary of hook hints
18+
/// An immutable dictionary of hook hints
1919
/// </summary>
2020
public IImmutableDictionary<string, object> HookHints { get; }
2121

src/OpenFeature/Model/ImmutableMetadata.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Collections.Generic;
22
using System.Collections.Immutable;
33

4-
#nullable enable
54
namespace OpenFeature.Model;
65

76
/// <summary>

src/OpenFeature/Model/Value.cs

+9-9
Original file line numberDiff line numberDiff line change
@@ -139,49 +139,49 @@ public Value(Object value)
139139
public object? AsObject => this._innerValue;
140140

141141
/// <summary>
142-
/// Returns the underlying int value
143-
/// Value will be null if it isn't a integer
142+
/// Returns the underlying int value.
143+
/// Value will be null if it isn't an integer
144144
/// </summary>
145145
/// <returns>Value as int</returns>
146-
public int? AsInteger => this.IsNumber ? (int?)Convert.ToInt32((double?)this._innerValue) : null;
146+
public int? AsInteger => this.IsNumber ? Convert.ToInt32((double?)this._innerValue) : null;
147147

148148
/// <summary>
149-
/// Returns the underlying bool value
149+
/// Returns the underlying bool value.
150150
/// Value will be null if it isn't a bool
151151
/// </summary>
152152
/// <returns>Value as bool</returns>
153153
public bool? AsBoolean => this.IsBoolean ? (bool?)this._innerValue : null;
154154

155155
/// <summary>
156-
/// Returns the underlying double value
156+
/// Returns the underlying double value.
157157
/// Value will be null if it isn't a double
158158
/// </summary>
159159
/// <returns>Value as int</returns>
160160
public double? AsDouble => this.IsNumber ? (double?)this._innerValue : null;
161161

162162
/// <summary>
163-
/// Returns the underlying string value
163+
/// Returns the underlying string value.
164164
/// Value will be null if it isn't a string
165165
/// </summary>
166166
/// <returns>Value as string</returns>
167167
public string? AsString => this.IsString ? (string?)this._innerValue : null;
168168

169169
/// <summary>
170-
/// Returns the underlying Structure value
170+
/// Returns the underlying Structure value.
171171
/// Value will be null if it isn't a Structure
172172
/// </summary>
173173
/// <returns>Value as Structure</returns>
174174
public Structure? AsStructure => this.IsStructure ? (Structure?)this._innerValue : null;
175175

176176
/// <summary>
177-
/// Returns the underlying List value
177+
/// Returns the underlying List value.
178178
/// Value will be null if it isn't a List
179179
/// </summary>
180180
/// <returns>Value as List</returns>
181181
public IImmutableList<Value>? AsList => this.IsList ? (IImmutableList<Value>?)this._innerValue : null;
182182

183183
/// <summary>
184-
/// Returns the underlying DateTime value
184+
/// Returns the underlying DateTime value.
185185
/// Value will be null if it isn't a DateTime
186186
/// </summary>
187187
/// <returns>Value as DateTime</returns>

src/OpenFeature/OpenFeatureClient.cs

+6-9
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,8 @@ private async Task<FlagEvaluationDetails<T>> EvaluateFlagAsync<T>(
212212
var resolveValueDelegate = providerInfo.Item1;
213213
var provider = providerInfo.Item2;
214214

215-
// New up a evaluation context if one was not provided.
216-
if (context == null)
217-
{
218-
context = EvaluationContext.Empty;
219-
}
215+
// New up an evaluation context if one was not provided.
216+
context ??= EvaluationContext.Empty;
220217

221218
// merge api, client, and invocation context.
222219
var evaluationContext = Api.Instance.GetContext();
@@ -253,11 +250,11 @@ private async Task<FlagEvaluationDetails<T>> EvaluateFlagAsync<T>(
253250
var contextFromHooks = await this.TriggerBeforeHooksAsync(allHooks, hookContext, options, cancellationToken).ConfigureAwait(false);
254251

255252
// short circuit evaluation entirely if provider is in a bad state
256-
if (provider.Status == ProviderStatus.NotReady)
253+
if (provider.Status == ProviderStatus.NotReady)
257254
{
258255
throw new ProviderNotReadyException("Provider has not yet completed initialization.");
259-
}
260-
else if (provider.Status == ProviderStatus.Fatal)
256+
}
257+
else if (provider.Status == ProviderStatus.Fatal)
261258
{
262259
throw new ProviderFatalException("Provider is in an irrecoverable error state.");
263260
}
@@ -349,7 +346,7 @@ private async Task TriggerFinallyHooksAsync<T>(IReadOnlyList<Hook> hooks, HookCo
349346
}
350347
catch (Exception e)
351348
{
352-
this._logger.LogError(e, "Error while executing Finally hook {HookName}", hook.GetType().Name);
349+
this.FinallyHookError(hook.GetType().Name, e);
353350
}
354351
}
355352
}

src/OpenFeature/ProviderRepository.cs

+12-14
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ namespace OpenFeature
1414
/// <summary>
1515
/// This class manages the collection of providers, both default and named, contained by the API.
1616
/// </summary>
17-
internal sealed class ProviderRepository : IAsyncDisposable
17+
internal sealed partial class ProviderRepository : IAsyncDisposable
1818
{
19-
private ILogger _logger;
19+
private ILogger _logger = NullLogger<EventExecutor>.Instance;
2020

2121
private FeatureProvider _defaultProvider = new NoOpFeatureProvider();
2222

@@ -26,20 +26,15 @@ internal sealed class ProviderRepository : IAsyncDisposable
2626
/// The reader/writer locks is not disposed because the singleton instance should never be disposed.
2727
///
2828
/// Mutations of the _defaultProvider or _featureProviders are done within this lock even though
29-
/// _featureProvider is a concurrent collection. This is for a couple reasons, the first is that
29+
/// _featureProvider is a concurrent collection. This is for a couple of reasons, the first is that
3030
/// a provider should only be shutdown if it is not in use, and it could be in use as either a named or
3131
/// default provider.
3232
///
33-
/// The second is that a concurrent collection doesn't provide any ordering so we could check a provider
33+
/// The second is that a concurrent collection doesn't provide any ordering, so we could check a provider
3434
/// as it was being added or removed such as two concurrent calls to SetProvider replacing multiple instances
35-
/// of that provider under different names..
35+
/// of that provider under different names.
3636
private readonly ReaderWriterLockSlim _providersLock = new ReaderWriterLockSlim();
3737

38-
public ProviderRepository()
39-
{
40-
this._logger = NullLogger<EventExecutor>.Instance;
41-
}
42-
4338
public async ValueTask DisposeAsync()
4439
{
4540
using (this._providersLock)
@@ -201,15 +196,15 @@ private async Task ShutdownIfUnusedAsync(
201196
return;
202197
}
203198

204-
await SafeShutdownProviderAsync(targetProvider).ConfigureAwait(false);
199+
await this.SafeShutdownProviderAsync(targetProvider).ConfigureAwait(false);
205200
}
206201

207202
/// <remarks>
208203
/// <para>
209204
/// Shut down the provider and capture any exceptions thrown.
210205
/// </para>
211206
/// <para>
212-
/// The provider is set either to a name or default before the old provider it shutdown, so
207+
/// The provider is set either to a name or default before the old provider it shut down, so
213208
/// it would not be meaningful to emit an error.
214209
/// </para>
215210
/// </remarks>
@@ -226,7 +221,7 @@ private async Task SafeShutdownProviderAsync(FeatureProvider? targetProvider)
226221
}
227222
catch (Exception ex)
228223
{
229-
this._logger.LogError(ex, $"Error shutting down provider: {targetProvider.GetMetadata().Name}");
224+
this.ErrorShuttingDownProvider(targetProvider.GetMetadata()?.Name, ex);
230225
}
231226
}
232227

@@ -287,8 +282,11 @@ public async Task ShutdownAsync(Action<FeatureProvider, Exception>? afterError =
287282
foreach (var targetProvider in providers)
288283
{
289284
// We don't need to take any actions after shutdown.
290-
await SafeShutdownProviderAsync(targetProvider).ConfigureAwait(false);
285+
await this.SafeShutdownProviderAsync(targetProvider).ConfigureAwait(false);
291286
}
292287
}
288+
289+
[LoggerMessage(EventId = 105, Level = LogLevel.Error, Message = "Error shutting down provider: {TargetProviderName}`")]
290+
partial void ErrorShuttingDownProvider(string? targetProviderName, Exception exception);
293291
}
294292
}

0 commit comments

Comments
 (0)