Skip to content

Commit ce221a0

Browse files
author
Scott Bommarito
authored
UI for Feature Flags (#7052)
1 parent acc6497 commit ce221a0

28 files changed

Lines changed: 1737 additions & 401 deletions

src/NuGetGallery.Core/Features/FeatureFlagFileStorageService.cs

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -49,47 +49,27 @@ public FeatureFlagFileStorageService(
4949
public async Task<FeatureFlags> GetAsync()
5050
{
5151
using (var stream = await _storage.GetFileAsync(CoreConstants.Folders.ContentFolderName, CoreConstants.FeatureFlagsFileName))
52-
using (var streamReader = new StreamReader(stream))
53-
using (var reader = new JsonTextReader(streamReader))
5452
{
55-
return Serializer.Deserialize<FeatureFlags>(reader);
53+
return ReadFeatureFlagsFromStream(stream);
5654
}
5755
}
5856

5957
public async Task<FeatureFlagReference> GetReferenceAsync()
6058
{
6159
var reference = await _storage.GetFileReferenceAsync(CoreConstants.Folders.ContentFolderName, CoreConstants.FeatureFlagsFileName);
6260

63-
string json;
64-
using (var stream = reference.OpenRead())
65-
using (var streamReader = new StreamReader(stream))
66-
{
67-
json = await streamReader.ReadToEndAsync();
68-
}
69-
7061
return new FeatureFlagReference(
71-
PrettifyJson(json),
62+
ReadFeatureFlagsFromStream(reference.OpenRead()),
7263
reference.ContentId);
7364
}
7465

75-
public async Task<FeatureFlagSaveResult> TrySaveAsync(string flagsJson, string contentId)
66+
private FeatureFlags ReadFeatureFlagsFromStream(Stream stream)
7667
{
77-
// Ensure the feature flags are valid before saving them.
78-
FeatureFlags flags;
79-
try
80-
{
81-
using (var reader = new StringReader(flagsJson))
82-
using (var jsonReader = new JsonTextReader(reader))
83-
{
84-
flags = Serializer.Deserialize<FeatureFlags>(jsonReader);
85-
}
86-
}
87-
catch (JsonException e)
68+
using (var streamReader = new StreamReader(stream))
69+
using (var reader = new JsonTextReader(streamReader))
8870
{
89-
return FeatureFlagSaveResult.Invalid(e.Message);
71+
return Serializer.Deserialize<FeatureFlags>(reader);
9072
}
91-
92-
return await TrySaveAsync(flags, contentId);
9373
}
9474

9575
public async Task RemoveUserAsync(User user)
@@ -122,7 +102,7 @@ public async Task RemoveUserAsync(User user)
122102
f => RemoveUser(f.Value, user)));
123103

124104
var saveResult = await TrySaveAsync(result, reference.ContentId);
125-
if (saveResult.Type == FeatureFlagSaveResultType.Ok)
105+
if (saveResult == FeatureFlagSaveResult.Ok)
126106
{
127107
return;
128108
}
@@ -137,7 +117,7 @@ public async Task RemoveUserAsync(User user)
137117
throw new InvalidOperationException($"Unable to remove user from feature flags after {MaxRemoveUserAttempts} attempts");
138118
}
139119

140-
private async Task<FeatureFlagSaveResult> TrySaveAsync(FeatureFlags flags, string contentId)
120+
public async Task<FeatureFlagSaveResult> TrySaveAsync(FeatureFlags flags, string contentId)
141121
{
142122
var accessCondition = AccessConditionWrapper.GenerateIfMatchCondition(contentId);
143123

@@ -170,10 +150,5 @@ private Flight RemoveUser(Flight flight, User user)
170150
flight.Accounts.Where(a => !a.Equals(user.Username, StringComparison.OrdinalIgnoreCase)).ToList(),
171151
flight.Domains);
172152
}
173-
174-
private string PrettifyJson(string json)
175-
{
176-
return JToken.Parse(json).ToString(Formatting.Indented);
177-
}
178153
}
179154
}

src/NuGetGallery.Core/Features/FeatureFlagReference.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using NuGet.Services.FeatureFlags;
56

67
namespace NuGetGallery.Features
78
{
@@ -10,26 +11,21 @@ namespace NuGetGallery.Features
1011
/// </summary>
1112
public class FeatureFlagReference
1213
{
13-
public FeatureFlagReference(string flagsJson, string contentId)
14+
public FeatureFlagReference(FeatureFlags flags, string contentId)
1415
{
15-
if (string.IsNullOrEmpty(flagsJson))
16-
{
17-
throw new ArgumentException(nameof(flagsJson));
18-
}
19-
2016
if (string.IsNullOrEmpty(contentId))
2117
{
2218
throw new ArgumentException(nameof(contentId));
2319
}
2420

25-
FlagsJson = flagsJson;
21+
Flags = flags ?? throw new ArgumentException(nameof(flags));
2622
ContentId = contentId;
2723
}
2824

2925
/// <summary>
3026
/// The feature flag's content, serialized as JSON.
3127
/// </summary>
32-
public string FlagsJson { get; }
28+
public FeatureFlags Flags { get; }
3329

3430
/// <summary>
3531
/// The feature flag's ETag.

src/NuGetGallery.Core/Features/FeatureFlagSaveResult.cs

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,16 @@ namespace NuGetGallery.Features
66
/// <summary>
77
/// The result of calling <see cref="FeatureFlagFileStorageService.TrySaveAsync(string, string)"/>.
88
/// </summary>
9-
public class FeatureFlagSaveResult
9+
public enum FeatureFlagSaveResult
1010
{
11-
private FeatureFlagSaveResult(FeatureFlagSaveResultType type, string message = null)
12-
{
13-
Type = type;
14-
Message = message ?? string.Empty;
15-
}
16-
17-
public static readonly FeatureFlagSaveResult Ok = new FeatureFlagSaveResult(FeatureFlagSaveResultType.Ok);
18-
public static readonly FeatureFlagSaveResult Conflict = new FeatureFlagSaveResult(FeatureFlagSaveResultType.Conflict);
19-
20-
public static FeatureFlagSaveResult Invalid(string message)
21-
{
22-
return new FeatureFlagSaveResult(FeatureFlagSaveResultType.Invalid, message);
23-
}
24-
2511
/// <summary>
26-
/// An error code explaining whether the save operation succeeded.
12+
/// The flags were saved successfully.
2713
/// </summary>
28-
public FeatureFlagSaveResultType Type { get; }
14+
Ok,
2915

3016
/// <summary>
31-
/// A non-null string explaining the result. Empty unless <see cref="Type"/> is <see cref="FeatureFlagSaveResultType.Invalid"/>.
17+
/// The flags were modified by someone else.
3218
/// </summary>
33-
public string Message { get; }
19+
Conflict,
3420
}
3521
}

src/NuGetGallery.Core/Features/FeatureFlagSaveResultType.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/NuGetGallery.Core/Features/IEditableFeatureFlagStorageService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ public interface IEditableFeatureFlagStorageService : IFeatureFlagStorageService
2020
/// <summary>
2121
/// Try to update the feature flags.
2222
/// </summary>
23-
/// <param name="flags">The feature flags serialized in JSON.</param>
23+
/// <param name="flags">The feature flags.</param>
2424
/// <param name="contentId">The feature flag's ETag.</param>
2525
/// <returns>The result of the save operation.</returns>
26-
Task<FeatureFlagSaveResult> TrySaveAsync(string flags, string contentId);
26+
Task<FeatureFlagSaveResult> TrySaveAsync(FeatureFlags flags, string contentId);
2727

2828
/// <summary>
2929
/// Remove the user from the feature flags if needed. This may throw on failure.

src/NuGetGallery.Core/NuGetGallery.Core.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@
136136
<Compile Include="Features\FeatureFlagClientExtensions.cs" />
137137
<Compile Include="Features\FeatureFlagReference.cs" />
138138
<Compile Include="Features\FeatureFlagSaveResult.cs" />
139-
<Compile Include="Features\FeatureFlagSaveResultType.cs" />
140139
<Compile Include="Features\IEditableFeatureFlagStorageService.cs" />
141140
<Compile Include="ICloudStorageStatusDependency.cs" />
142141
<Compile Include="Infrastructure\AzureEntityList.cs" />

0 commit comments

Comments
 (0)