Skip to content

Commit 5a5312c

Browse files
authored
chore!: Enable nullable reference types (open-feature#253)
<!-- Please use this template for your pull request. --> <!-- Please use the sections that you need and delete other sections --> ## Enable nullable reference types <!-- add the description of the PR here --> - This PR enables the nullable validation and treats warnings as errors. ### Related Issues <!-- add here the GitHub issue that this PR resolves if applicable --> Fixes open-feature#208 ### Notes <!-- any additional notes for this PR --> This PR turns on the nullable checks for the dotnet checks. This gives us a better and safer codebase since it checks for potential null exceptions. ### Breaking changes While this PR won't require changes to the exposed API, it might show some errors to the clients consuming it. --------- Signed-off-by: André Silva <[email protected]>
1 parent 9b9c3fd commit 5a5312c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+486
-272
lines changed

build/Common.props

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
77
<!-- Workaround for IDE0005 (Remove unnecessary usings/imports); see https://github.com/dotnet/roslyn/issues/41640 -->
88
<NoWarn>EnableGenerateDocumentationFile</NoWarn>
9+
<Nullable>enable</Nullable>
10+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
911
</PropertyGroup>
1012

1113
<PropertyGroup Condition="'$(Configuration)'=='Debug'">

src/OpenFeature/Api.cs

+8-4
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public void SetProvider(FeatureProvider featureProvider)
5454
/// </summary>
5555
/// <remarks>The provider cannot be set to null. Attempting to set the provider to null has no effect.</remarks>
5656
/// <param name="featureProvider">Implementation of <see cref="FeatureProvider"/></param>
57-
public async Task SetProviderAsync(FeatureProvider featureProvider)
57+
public async Task SetProviderAsync(FeatureProvider? featureProvider)
5858
{
5959
this._eventExecutor.RegisterDefaultFeatureProvider(featureProvider);
6060
await this._repository.SetProvider(featureProvider, this.GetContext()).ConfigureAwait(false);
@@ -80,6 +80,10 @@ public void SetProvider(string clientName, FeatureProvider featureProvider)
8080
/// <param name="featureProvider">Implementation of <see cref="FeatureProvider"/></param>
8181
public async Task SetProviderAsync(string clientName, FeatureProvider featureProvider)
8282
{
83+
if (string.IsNullOrWhiteSpace(clientName))
84+
{
85+
throw new ArgumentNullException(nameof(clientName));
86+
}
8387
this._eventExecutor.RegisterClientFeatureProvider(clientName, featureProvider);
8488
await this._repository.SetProvider(clientName, featureProvider, this.GetContext()).ConfigureAwait(false);
8589
}
@@ -138,8 +142,8 @@ public FeatureProvider GetProvider(string clientName)
138142
/// <param name="logger">Logger instance used by client</param>
139143
/// <param name="context">Context given to this client</param>
140144
/// <returns><see cref="FeatureClient"/></returns>
141-
public FeatureClient GetClient(string name = null, string version = null, ILogger logger = null,
142-
EvaluationContext context = null) =>
145+
public FeatureClient GetClient(string? name = null, string? version = null, ILogger? logger = null,
146+
EvaluationContext? context = null) =>
143147
new FeatureClient(name, version, logger, context);
144148

145149
/// <summary>
@@ -200,7 +204,7 @@ public void AddHooks(IEnumerable<Hook> hooks)
200204
/// Sets the global <see cref="EvaluationContext"/>
201205
/// </summary>
202206
/// <param name="context">The <see cref="EvaluationContext"/> to set</param>
203-
public void SetContext(EvaluationContext context)
207+
public void SetContext(EvaluationContext? context)
204208
{
205209
this._evaluationContextLock.EnterWriteLock();
206210
try

src/OpenFeature/Error/FeatureProviderException.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class FeatureProviderException : Exception
2020
/// <param name="errorType">Common error types <see cref="ErrorType"/></param>
2121
/// <param name="message">Exception message</param>
2222
/// <param name="innerException">Optional inner exception</param>
23-
public FeatureProviderException(ErrorType errorType, string message = null, Exception innerException = null)
23+
public FeatureProviderException(ErrorType errorType, string? message = null, Exception? innerException = null)
2424
: base(message, innerException)
2525
{
2626
this.ErrorType = errorType;

src/OpenFeature/Error/FlagNotFoundException.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class FlagNotFoundException : FeatureProviderException
1515
/// </summary>
1616
/// <param name="message">Exception message</param>
1717
/// <param name="innerException">Optional inner exception</param>
18-
public FlagNotFoundException(string message = null, Exception innerException = null)
18+
public FlagNotFoundException(string? message = null, Exception? innerException = null)
1919
: base(ErrorType.FlagNotFound, message, innerException)
2020
{
2121
}

src/OpenFeature/Error/GeneralException.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class GeneralException : FeatureProviderException
1515
/// </summary>
1616
/// <param name="message">Exception message</param>
1717
/// <param name="innerException">Optional inner exception</param>
18-
public GeneralException(string message = null, Exception innerException = null)
18+
public GeneralException(string? message = null, Exception? innerException = null)
1919
: base(ErrorType.General, message, innerException)
2020
{
2121
}

src/OpenFeature/Error/InvalidContextException.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class InvalidContextException : FeatureProviderException
1515
/// </summary>
1616
/// <param name="message">Exception message</param>
1717
/// <param name="innerException">Optional inner exception</param>
18-
public InvalidContextException(string message = null, Exception innerException = null)
18+
public InvalidContextException(string? message = null, Exception? innerException = null)
1919
: base(ErrorType.InvalidContext, message, innerException)
2020
{
2121
}

src/OpenFeature/Error/ParseErrorException.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class ParseErrorException : FeatureProviderException
1515
/// </summary>
1616
/// <param name="message">Exception message</param>
1717
/// <param name="innerException">Optional inner exception</param>
18-
public ParseErrorException(string message = null, Exception innerException = null)
18+
public ParseErrorException(string? message = null, Exception? innerException = null)
1919
: base(ErrorType.ParseError, message, innerException)
2020
{
2121
}

src/OpenFeature/Error/ProviderNotReadyException.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class ProviderNotReadyException : FeatureProviderException
1515
/// </summary>
1616
/// <param name="message">Exception message</param>
1717
/// <param name="innerException">Optional inner exception</param>
18-
public ProviderNotReadyException(string message = null, Exception innerException = null)
18+
public ProviderNotReadyException(string? message = null, Exception? innerException = null)
1919
: base(ErrorType.ProviderNotReady, message, innerException)
2020
{
2121
}

src/OpenFeature/Error/TargetingKeyMissingException.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class TargetingKeyMissingException : FeatureProviderException
1515
/// </summary>
1616
/// <param name="message">Exception message</param>
1717
/// <param name="innerException">Optional inner exception</param>
18-
public TargetingKeyMissingException(string message = null, Exception innerException = null)
18+
public TargetingKeyMissingException(string? message = null, Exception? innerException = null)
1919
: base(ErrorType.TargetingKeyMissing, message, innerException)
2020
{
2121
}

src/OpenFeature/Error/TypeMismatchException.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class TypeMismatchException : FeatureProviderException
1515
/// </summary>
1616
/// <param name="message">Exception message</param>
1717
/// <param name="innerException">Optional inner exception</param>
18-
public TypeMismatchException(string message = null, Exception innerException = null)
18+
public TypeMismatchException(string? message = null, Exception? innerException = null)
1919
: base(ErrorType.TypeMismatch, message, innerException)
2020
{
2121
}

src/OpenFeature/EventExecutor.cs

+18-18
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ internal class EventExecutor : IAsyncDisposable
1414
{
1515
private readonly object _lockObj = new object();
1616
public readonly Channel<object> EventChannel = Channel.CreateBounded<object>(1);
17-
private FeatureProvider _defaultProvider;
17+
private FeatureProvider? _defaultProvider;
1818
private readonly Dictionary<string, FeatureProvider> _namedProviderReferences = new Dictionary<string, FeatureProvider>();
1919
private readonly List<FeatureProvider> _activeSubscriptions = new List<FeatureProvider>();
2020

@@ -99,7 +99,7 @@ internal void RemoveClientHandler(string client, ProviderEventTypes type, EventH
9999
}
100100
}
101101

102-
internal void RegisterDefaultFeatureProvider(FeatureProvider provider)
102+
internal void RegisterDefaultFeatureProvider(FeatureProvider? provider)
103103
{
104104
if (provider == null)
105105
{
@@ -115,7 +115,7 @@ internal void RegisterDefaultFeatureProvider(FeatureProvider provider)
115115
}
116116
}
117117

118-
internal void RegisterClientFeatureProvider(string client, FeatureProvider provider)
118+
internal void RegisterClientFeatureProvider(string client, FeatureProvider? provider)
119119
{
120120
if (provider == null)
121121
{
@@ -124,7 +124,7 @@ internal void RegisterClientFeatureProvider(string client, FeatureProvider provi
124124
lock (this._lockObj)
125125
{
126126
var newProvider = provider;
127-
FeatureProvider oldProvider = null;
127+
FeatureProvider? oldProvider = null;
128128
if (this._namedProviderReferences.TryGetValue(client, out var foundOldProvider))
129129
{
130130
oldProvider = foundOldProvider;
@@ -136,7 +136,7 @@ internal void RegisterClientFeatureProvider(string client, FeatureProvider provi
136136
}
137137
}
138138

139-
private void StartListeningAndShutdownOld(FeatureProvider newProvider, FeatureProvider oldProvider)
139+
private void StartListeningAndShutdownOld(FeatureProvider newProvider, FeatureProvider? oldProvider)
140140
{
141141
// check if the provider is already active - if not, we need to start listening for its emitted events
142142
if (!this.IsProviderActive(newProvider))
@@ -174,7 +174,7 @@ private bool IsProviderActive(FeatureProvider providerRef)
174174
return this._activeSubscriptions.Contains(providerRef);
175175
}
176176

177-
private void EmitOnRegistration(FeatureProvider provider, ProviderEventTypes eventType, EventHandlerDelegate handler)
177+
private void EmitOnRegistration(FeatureProvider? provider, ProviderEventTypes eventType, EventHandlerDelegate handler)
178178
{
179179
if (provider == null)
180180
{
@@ -202,22 +202,22 @@ private void EmitOnRegistration(FeatureProvider provider, ProviderEventTypes eve
202202
{
203203
handler.Invoke(new ProviderEventPayload
204204
{
205-
ProviderName = provider.GetMetadata()?.Name,
205+
ProviderName = provider.GetMetadata().Name,
206206
Type = eventType,
207207
Message = message
208208
});
209209
}
210210
catch (Exception exc)
211211
{
212-
this.Logger?.LogError("Error running handler: " + exc);
212+
this.Logger.LogError(exc, "Error running handler");
213213
}
214214
}
215215
}
216216

217-
private async void ProcessFeatureProviderEventsAsync(object providerRef)
217+
private async void ProcessFeatureProviderEventsAsync(object? providerRef)
218218
{
219-
var typedProviderRef = (FeatureProvider)providerRef;
220-
if (typedProviderRef.GetEventChannel() is not { Reader: { } reader })
219+
var typedProviderRef = (FeatureProvider?)providerRef;
220+
if (typedProviderRef?.GetEventChannel() is not { Reader: { } reader })
221221
{
222222
return;
223223
}
@@ -249,7 +249,7 @@ private async void ProcessEventAsync()
249249
case Event e:
250250
lock (this._lockObj)
251251
{
252-
if (this._apiHandlers.TryGetValue(e.EventPayload.Type, out var eventHandlers))
252+
if (e.EventPayload?.Type != null && this._apiHandlers.TryGetValue(e.EventPayload.Type, out var eventHandlers))
253253
{
254254
foreach (var eventHandler in eventHandlers)
255255
{
@@ -260,11 +260,11 @@ private async void ProcessEventAsync()
260260
// look for client handlers and call invoke method there
261261
foreach (var keyAndValue in this._namedProviderReferences)
262262
{
263-
if (keyAndValue.Value == e.Provider)
263+
if (keyAndValue.Value == e.Provider && keyAndValue.Key != null)
264264
{
265265
if (this._clientHandlers.TryGetValue(keyAndValue.Key, out var clientRegistry))
266266
{
267-
if (clientRegistry.TryGetValue(e.EventPayload.Type, out var clientEventHandlers))
267+
if (e.EventPayload?.Type != null && clientRegistry.TryGetValue(e.EventPayload.Type, out var clientEventHandlers))
268268
{
269269
foreach (var eventHandler in clientEventHandlers)
270270
{
@@ -288,7 +288,7 @@ private async void ProcessEventAsync()
288288
// if there is an association for the client to a specific feature provider, then continue
289289
continue;
290290
}
291-
if (keyAndValues.Value.TryGetValue(e.EventPayload.Type, out var clientEventHandlers))
291+
if (e.EventPayload?.Type != null && keyAndValues.Value.TryGetValue(e.EventPayload.Type, out var clientEventHandlers))
292292
{
293293
foreach (var eventHandler in clientEventHandlers)
294294
{
@@ -311,7 +311,7 @@ private void InvokeEventHandler(EventHandlerDelegate eventHandler, Event e)
311311
}
312312
catch (Exception exc)
313313
{
314-
this.Logger?.LogError("Error running handler: " + exc);
314+
this.Logger.LogError(exc, "Error running handler");
315315
}
316316
}
317317

@@ -325,7 +325,7 @@ public async Task Shutdown()
325325

326326
internal class Event
327327
{
328-
internal FeatureProvider Provider { get; set; }
329-
internal ProviderEventPayload EventPayload { get; set; }
328+
internal FeatureProvider? Provider { get; set; }
329+
internal ProviderEventPayload? EventPayload { get; set; }
330330
}
331331
}

src/OpenFeature/Extension/EnumExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ internal static class EnumExtensions
99
public static string GetDescription(this Enum value)
1010
{
1111
var field = value.GetType().GetField(value.ToString());
12-
var attribute = field.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() as DescriptionAttribute;
12+
var attribute = field?.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() as DescriptionAttribute;
1313
return attribute?.Description ?? value.ToString();
1414
}
1515
}

src/OpenFeature/FeatureProvider.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public abstract class FeatureProvider
4545
/// <param name="context"><see cref="EvaluationContext"/></param>
4646
/// <returns><see cref="ResolutionDetails{T}"/></returns>
4747
public abstract Task<ResolutionDetails<bool>> ResolveBooleanValue(string flagKey, bool defaultValue,
48-
EvaluationContext context = null);
48+
EvaluationContext? context = null);
4949

5050
/// <summary>
5151
/// Resolves a string feature flag
@@ -55,7 +55,7 @@ public abstract Task<ResolutionDetails<bool>> ResolveBooleanValue(string flagKey
5555
/// <param name="context"><see cref="EvaluationContext"/></param>
5656
/// <returns><see cref="ResolutionDetails{T}"/></returns>
5757
public abstract Task<ResolutionDetails<string>> ResolveStringValue(string flagKey, string defaultValue,
58-
EvaluationContext context = null);
58+
EvaluationContext? context = null);
5959

6060
/// <summary>
6161
/// Resolves a integer feature flag
@@ -65,7 +65,7 @@ public abstract Task<ResolutionDetails<string>> ResolveStringValue(string flagKe
6565
/// <param name="context"><see cref="EvaluationContext"/></param>
6666
/// <returns><see cref="ResolutionDetails{T}"/></returns>
6767
public abstract Task<ResolutionDetails<int>> ResolveIntegerValue(string flagKey, int defaultValue,
68-
EvaluationContext context = null);
68+
EvaluationContext? context = null);
6969

7070
/// <summary>
7171
/// Resolves a double feature flag
@@ -75,7 +75,7 @@ public abstract Task<ResolutionDetails<int>> ResolveIntegerValue(string flagKey,
7575
/// <param name="context"><see cref="EvaluationContext"/></param>
7676
/// <returns><see cref="ResolutionDetails{T}"/></returns>
7777
public abstract Task<ResolutionDetails<double>> ResolveDoubleValue(string flagKey, double defaultValue,
78-
EvaluationContext context = null);
78+
EvaluationContext? context = null);
7979

8080
/// <summary>
8181
/// Resolves a structured feature flag
@@ -85,7 +85,7 @@ public abstract Task<ResolutionDetails<double>> ResolveDoubleValue(string flagKe
8585
/// <param name="context"><see cref="EvaluationContext"/></param>
8686
/// <returns><see cref="ResolutionDetails{T}"/></returns>
8787
public abstract Task<ResolutionDetails<Value>> ResolveStructureValue(string flagKey, Value defaultValue,
88-
EvaluationContext context = null);
88+
EvaluationContext? context = null);
8989

9090
/// <summary>
9191
/// Get the status of the provider.

src/OpenFeature/Hook.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public abstract class Hook
2929
/// <typeparam name="T">Flag value type (bool|number|string|object)</typeparam>
3030
/// <returns>Modified EvaluationContext that is used for the flag evaluation</returns>
3131
public virtual Task<EvaluationContext> Before<T>(HookContext<T> context,
32-
IReadOnlyDictionary<string, object> hints = null)
32+
IReadOnlyDictionary<string, object>? hints = null)
3333
{
3434
return Task.FromResult(EvaluationContext.Empty);
3535
}
@@ -42,7 +42,7 @@ public virtual Task<EvaluationContext> Before<T>(HookContext<T> context,
4242
/// <param name="hints">Caller provided data</param>
4343
/// <typeparam name="T">Flag value type (bool|number|string|object)</typeparam>
4444
public virtual Task After<T>(HookContext<T> context, FlagEvaluationDetails<T> details,
45-
IReadOnlyDictionary<string, object> hints = null)
45+
IReadOnlyDictionary<string, object>? hints = null)
4646
{
4747
return Task.CompletedTask;
4848
}
@@ -55,7 +55,7 @@ public virtual Task After<T>(HookContext<T> context, FlagEvaluationDetails<T> de
5555
/// <param name="hints">Caller provided data</param>
5656
/// <typeparam name="T">Flag value type (bool|number|string|object)</typeparam>
5757
public virtual Task Error<T>(HookContext<T> context, Exception error,
58-
IReadOnlyDictionary<string, object> hints = null)
58+
IReadOnlyDictionary<string, object>? hints = null)
5959
{
6060
return Task.CompletedTask;
6161
}
@@ -66,7 +66,7 @@ public virtual Task Error<T>(HookContext<T> context, Exception error,
6666
/// <param name="context">Provides context of innovation</param>
6767
/// <param name="hints">Caller provided data</param>
6868
/// <typeparam name="T">Flag value type (bool|number|string|object)</typeparam>
69-
public virtual Task Finally<T>(HookContext<T> context, IReadOnlyDictionary<string, object> hints = null)
69+
public virtual Task Finally<T>(HookContext<T> context, IReadOnlyDictionary<string, object>? hints = null)
7070
{
7171
return Task.CompletedTask;
7272
}

0 commit comments

Comments
 (0)