Skip to content

Commit 77f1e28

Browse files
committed
Merge manifest nodes on case insensitive OSes
1 parent f09b681 commit 77f1e28

File tree

2 files changed

+120
-4
lines changed

2 files changed

+120
-4
lines changed

src/Hosting/Hosting/test/StaticWebAssets/ManifestStaticWebAssetsFileProviderTests.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,74 @@ public void GetFileInfoPrefixRespectsOsCaseSensitivity()
5757
Assert.Equal(expectedResult, file.Exists);
5858
}
5959

60+
[Theory]
61+
[InlineData("/img/icon.png", true)]
62+
[InlineData("/Img/hero.gif", true)]
63+
// Note that we've changed the casing of the first segment
64+
[InlineData("/Img/icon.png", false)]
65+
[InlineData("/img/hero.gif", false)]
66+
public void ParseWorksWithNodesThatOnlyDifferOnCasing(string path, bool exists)
67+
{
68+
exists = exists | OperatingSystem.IsWindows();
69+
// Arrange
70+
using var memoryStream = new MemoryStream();
71+
using var writer = new StreamWriter(memoryStream);
72+
writer.Write(@"{
73+
""ContentRoots"": [
74+
""D:\\path\\"",
75+
""D:\\other\\""
76+
],
77+
""Root"": {
78+
""Children"": {
79+
""img"": {
80+
""Children"": {
81+
""icon.png"": {
82+
""Asset"": {
83+
""ContentRootIndex"": 0,
84+
""SubPath"": ""icon.png""
85+
}
86+
}
87+
}
88+
},
89+
""Img"": {
90+
""Children"": {
91+
""hero.gif"": {
92+
""Asset"": {
93+
""ContentRootIndex"": 1,
94+
""SubPath"": ""hero.gif""
95+
}
96+
}
97+
}
98+
}
99+
}
100+
}
101+
}");
102+
var first = new Mock<IFileProvider>();
103+
first.Setup(s => s.GetFileInfo("icon.png")).Returns(new TestFileInfo() { Name = "icon.png", Exists = true });
104+
var second = new Mock<IFileProvider>();
105+
second.Setup(s => s.GetFileInfo("hero.gif")).Returns(new TestFileInfo() { Name = "hero.gif", Exists = true });
106+
107+
writer.Flush();
108+
memoryStream.Seek(0, SeekOrigin.Begin);
109+
var manifest = ManifestStaticWebAssetFileProvider.StaticWebAssetManifest.Parse(memoryStream);
110+
var comparer = ManifestStaticWebAssetFileProvider.StaticWebAssetManifest.PathComparer;
111+
112+
var provider = new ManifestStaticWebAssetFileProvider(
113+
manifest,
114+
contentRoot => contentRoot switch
115+
{
116+
"D:\\path\\" => first.Object,
117+
"D:\\other\\" => second.Object,
118+
_ => throw new InvalidOperationException("Unknown provider")
119+
});
120+
121+
// Act
122+
var file = provider.GetFileInfo(path);
123+
124+
// Assert
125+
Assert.Equal(exists, file.Exists);
126+
}
127+
60128
[Fact]
61129
public void CanFindFileListedOnTheManifest()
62130
{

src/Shared/StaticWebAssets/ManifestStaticWebAssetFileProvider.cs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
using System.Collections;
55
using System.Diagnostics.CodeAnalysis;
6-
using System.Linq;
76
using System.Text.Json;
87
using System.Text.Json.Serialization;
98
using Microsoft.Extensions.FileProviders;
@@ -367,9 +366,58 @@ private sealed class OSBasedCaseConverter : JsonConverter<Dictionary<string, Sta
367366
{
368367
public override Dictionary<string, StaticWebAssetNode> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
369368
{
370-
return new Dictionary<string, StaticWebAssetNode>(
371-
JsonSerializer.Deserialize<IDictionary<string, StaticWebAssetNode>>(ref reader, options)!,
372-
StaticWebAssetManifest.PathComparer);
369+
var parsed = JsonSerializer.Deserialize<IDictionary<string, StaticWebAssetNode>>(ref reader, options)!;
370+
var result = new Dictionary<string, StaticWebAssetNode>(StaticWebAssetManifest.PathComparer);
371+
MergeChildren(parsed, result);
372+
return result;
373+
374+
static void MergeChildren(
375+
IDictionary<string, StaticWebAssetNode> newChildren,
376+
IDictionary<string, StaticWebAssetNode> existing)
377+
{
378+
foreach (var (key, value) in newChildren)
379+
{
380+
if (!existing.TryGetValue(key, out var existingNode))
381+
{
382+
existing.Add(key, value);
383+
}
384+
else
385+
{
386+
if (value.Patterns != null)
387+
{
388+
if (existingNode.Patterns == null)
389+
{
390+
existingNode.Patterns = value.Patterns;
391+
}
392+
else
393+
{
394+
if (value.Patterns.Length > 0)
395+
{
396+
var newList = new StaticWebAssetPattern[existingNode.Patterns.Length + value.Patterns.Length];
397+
existingNode.Patterns.CopyTo(newList, 0);
398+
value.Patterns.CopyTo(newList, existingNode.Patterns.Length);
399+
existingNode.Patterns = newList;
400+
}
401+
}
402+
}
403+
404+
if (value.Children != null)
405+
{
406+
if (existingNode.Children == null)
407+
{
408+
existingNode.Children = value.Children;
409+
}
410+
else
411+
{
412+
if (value.Children.Count > 0)
413+
{
414+
MergeChildren(value.Children, existingNode.Children);
415+
}
416+
}
417+
}
418+
}
419+
}
420+
}
373421
}
374422

375423
public override void Write(Utf8JsonWriter writer, Dictionary<string, StaticWebAssetNode> value, JsonSerializerOptions options)

0 commit comments

Comments
 (0)