Skip to content

Commit 425e6d2

Browse files
committed
refactor(repeater): simplify FromDictionary method
1 parent fb0478d commit 425e6d2

9 files changed

+94
-128
lines changed
Lines changed: 47 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.IO;
43
using System.Linq;
54
using System.Net.Http;
6-
using System.Reflection;
7-
using System.Runtime.Serialization;
85
using MessagePack;
96
using SecTester.Repeater.Internal;
107
using SecTester.Repeater.Runners;
@@ -20,75 +17,66 @@ public record IncomingRequest(Uri Url) : IRequest
2017
private const string BodyKey = "body";
2118
private const string ProtocolKey = "protocol";
2219

23-
private static readonly Dictionary<string, Protocol> ProtocolEntries = typeof(Protocol)
24-
.GetFields(BindingFlags.Public | BindingFlags.Static)
25-
.Select(field => new
26-
{
27-
Value = (Protocol)field.GetValue(null),
28-
StringValue = field.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? field.Name
29-
})
30-
.ToDictionary(x => MessagePackNamingPolicy.SnakeCase.ConvertName(x.StringValue), x => x.Value);
31-
32-
[Key(ProtocolKey)]
33-
public Protocol Protocol { get; set; } = Protocol.Http;
20+
[Key(ProtocolKey)] public Protocol Protocol { get; set; } = Protocol.Http;
3421

35-
private IEnumerable<KeyValuePair<string, IEnumerable<string>>> _headers = Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>();
36-
37-
[Key(HeadersKey)]
38-
public IEnumerable<KeyValuePair<string, IEnumerable<string>>> Headers
39-
{
40-
get => _headers;
41-
// ADHOC: convert from a kind of assignable type to formatter resolvable type
42-
set => _headers = value.AsEnumerable();
43-
}
22+
[Key(HeadersKey)] public IEnumerable<KeyValuePair<string, IEnumerable<string>>> Headers { get; set; } = new Dictionary<string, IEnumerable<string>>();
4423

45-
[Key(BodyKey)]
46-
public string? Body { get; set; }
24+
[Key(BodyKey)] public string? Body { get; set; }
4725

48-
[Key(MethodKey)]
49-
public HttpMethod Method { get; set; } = HttpMethod.Get;
26+
[Key(MethodKey)] public HttpMethod Method { get; set; } = HttpMethod.Get;
5027

51-
[Key(UrlKey)]
52-
public Uri Url { get; set; } = Url ?? throw new ArgumentNullException(nameof(Url));
28+
[Key(UrlKey)] public Uri Url { get; set; } = Url ?? throw new ArgumentNullException(nameof(Url));
5329

5430
public static IncomingRequest FromDictionary(Dictionary<object, object> dictionary)
5531
{
56-
var protocol = !dictionary.ContainsKey(ProtocolKey) || (dictionary.TryGetValue(ProtocolKey, out var p1) && p1 is null)
57-
? Protocol.Http
58-
: dictionary.TryGetValue(ProtocolKey, out var p2) && p2 is string && ProtocolEntries.TryGetValue(p2.ToString(), out var e)
59-
? e
60-
: throw new InvalidDataException(FormatPropertyError(ProtocolKey));
61-
62-
var uri = dictionary.TryGetValue(UrlKey, out var u) && u is string
63-
? new Uri(u.ToString())
64-
: throw new InvalidDataException(FormatPropertyError(UrlKey));
65-
66-
var method = dictionary.TryGetValue(MethodKey, out var m) && m is string
67-
? new HttpMethod(m.ToString())
68-
: HttpMethod.Get;
32+
var protocol = GetProtocolFromDictionary(dictionary);
33+
var headers = GetHeadersFromDictionary(dictionary);
34+
var body = GetBodyFromDictionary(dictionary);
35+
var method = GetMethodFromDictionary(dictionary);
36+
var url = GetUrlFromDictionary(dictionary);
6937

70-
var body = dictionary.TryGetValue(BodyKey, out var b) && b is string ? b.ToString() : null;
71-
72-
var headers = dictionary.TryGetValue(HeadersKey, out var h) && h is Dictionary<object, object> value
73-
? MapHeaders(value)
74-
: Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>();
75-
76-
return new IncomingRequest(uri)
38+
return new IncomingRequest(url!)
7739
{
7840
Protocol = protocol,
41+
Headers = headers,
7942
Body = body,
80-
Method = method,
81-
Headers = headers
43+
Method = method
8244
};
8345
}
8446

85-
private static IEnumerable<KeyValuePair<string, IEnumerable<string>>> MapHeaders(Dictionary<object, object> headers) =>
86-
headers.Select(kvp => kvp.Value switch
87-
{
88-
IEnumerable<object> strings => new KeyValuePair<string, IEnumerable<string>>(kvp.Key.ToString(), strings.Select(x => x.ToString())),
89-
null => new KeyValuePair<string, IEnumerable<string>>(kvp.Key.ToString(), Enumerable.Empty<string>()),
90-
_ => new KeyValuePair<string, IEnumerable<string>>(kvp.Key.ToString(), new[] { kvp.Value.ToString() })
91-
});
47+
private static Protocol GetProtocolFromDictionary(Dictionary<object, object> dictionary) =>
48+
dictionary.TryGetValue(ProtocolKey, out var protocolObj) && protocolObj is string protocolStr
49+
? (Protocol)Enum.Parse(typeof(Protocol), protocolStr, true)
50+
: Protocol.Http;
51+
52+
private static IEnumerable<KeyValuePair<string, IEnumerable<string>>> GetHeadersFromDictionary(Dictionary<object, object> dictionary) =>
53+
dictionary.TryGetValue(HeadersKey, out var headersObj) && headersObj is Dictionary<object, object> headersDict
54+
? ConvertToHeaders(headersDict)
55+
: new Dictionary<string, IEnumerable<string>>();
56+
57+
private static string? GetBodyFromDictionary(Dictionary<object, object> dictionary) =>
58+
dictionary.TryGetValue(BodyKey, out var bodyObj) ? bodyObj?.ToString() : null;
59+
60+
private static HttpMethod GetMethodFromDictionary(Dictionary<object, object> dictionary) =>
61+
dictionary.TryGetValue(MethodKey, out var methodObj) && methodObj is string methodStr
62+
? HttpMethods.Items.TryGetValue(methodStr, out var m) && m is not null
63+
? m
64+
: HttpMethod.Get
65+
: HttpMethod.Get;
9266

93-
private static string FormatPropertyError(string propName) => $"{propName} is either null or has an invalid data type or value";
67+
private static Uri? GetUrlFromDictionary(Dictionary<object, object> dictionary) =>
68+
dictionary.TryGetValue(UrlKey, out var urlObj) && urlObj is string urlStr
69+
? new Uri(urlStr)
70+
: null;
71+
72+
private static IEnumerable<KeyValuePair<string, IEnumerable<string>>> ConvertToHeaders(Dictionary<object, object> headers) =>
73+
headers.ToDictionary(
74+
kvp => kvp.Key.ToString()!,
75+
kvp => kvp.Value switch
76+
{
77+
IEnumerable<object> list => list.Select(v => v.ToString()!),
78+
string str => new[] { str },
79+
_ => Enumerable.Empty<string>()
80+
}
81+
);
9482
}

src/SecTester.Repeater/Bus/OutgoingResponse.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Collections.Generic;
2-
using System.Linq;
32
using MessagePack;
43
using SecTester.Repeater.Runners;
54

@@ -23,13 +22,6 @@ public record OutgoingResponse : IResponse
2322
[Key("errorCode")]
2423
public string? ErrorCode { get; set; }
2524

26-
private IEnumerable<KeyValuePair<string, IEnumerable<string>>> _headers = Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>();
27-
2825
[Key("headers")]
29-
public IEnumerable<KeyValuePair<string, IEnumerable<string>>> Headers
30-
{
31-
get => _headers;
32-
// ADHOC: convert from a kind of assignable type to formatter resolvable type
33-
set => _headers = value.AsEnumerable();
34-
}
26+
public IEnumerable<KeyValuePair<string, IEnumerable<string>>> Headers { get; set; } = new Dictionary<string, IEnumerable<string>>();
3527
}

src/SecTester.Repeater/Internal/DefaultMessagePackSerializerOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace SecTester.Repeater.Internal;
55

6-
internal class DefaultMessagePackSerializerOptions
6+
internal static class DefaultMessagePackSerializerOptions
77
{
88
internal static readonly MessagePackSerializerOptions Instance = new(
99
CompositeResolver.Create(
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net.Http;
5+
using System.Reflection;
6+
7+
namespace SecTester.Repeater.Internal;
8+
9+
public class HttpMethods
10+
{
11+
public static IDictionary<string, HttpMethod> Items { get; } = typeof(HttpMethod)
12+
.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
13+
.Where(x => x.PropertyType.IsAssignableFrom(typeof(HttpMethod)))
14+
.Select(x => x.GetValue(null))
15+
.Cast<HttpMethod>()
16+
.Concat(new List<HttpMethod>
17+
{
18+
new("PATCH"),
19+
new("COPY"),
20+
new("LINK"),
21+
new("UNLINK"),
22+
new("PURGE"),
23+
new("LOCK"),
24+
new("UNLOCK"),
25+
new("PROPFIND"),
26+
new("VIEW")
27+
})
28+
.Distinct()
29+
.ToDictionary(x => x.Method, x => x, StringComparer.InvariantCultureIgnoreCase);
30+
}

src/SecTester.Repeater/Internal/MessagePackHttpMethodFormatter.cs

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,11 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
41
using System.Net.Http;
5-
using System.Reflection;
62
using MessagePack;
73
using MessagePack.Formatters;
84

95
namespace SecTester.Repeater.Internal;
106

117
internal class MessagePackHttpMethodFormatter : IMessagePackFormatter<HttpMethod?>
128
{
13-
private static readonly IEnumerable<HttpMethod> BaseMethods = typeof(HttpMethod)
14-
.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
15-
.Where(x => x.PropertyType.IsAssignableFrom(typeof(HttpMethod)))
16-
.Select(x => x.GetValue(null))
17-
.Cast<HttpMethod>();
18-
19-
private static readonly IEnumerable<HttpMethod> CustomMethods = new List<HttpMethod>
20-
{
21-
new("PATCH"),
22-
new("COPY"),
23-
new("LINK"),
24-
new("UNLINK"),
25-
new("PURGE"),
26-
new("LOCK"),
27-
new("UNLOCK"),
28-
new("PROPFIND"),
29-
new("VIEW")
30-
};
31-
32-
private static readonly IDictionary<string, HttpMethod> Methods = BaseMethods.Concat(CustomMethods).Distinct()
33-
.ToDictionary(x => x.Method, x => x, StringComparer.InvariantCultureIgnoreCase);
349
public void Serialize(ref MessagePackWriter writer, HttpMethod? value, MessagePackSerializerOptions options)
3510
{
3611
if (null == value)
@@ -63,7 +38,7 @@ public void Serialize(ref MessagePackWriter writer, HttpMethod? value, MessagePa
6338
{
6439
var token = reader.ReadString();
6540

66-
if (token is null || !Methods.TryGetValue(token, out var method))
41+
if (token is null || !HttpMethods.Items.TryGetValue(token, out var method))
6742
{
6843
throw new MessagePackSerializationException(
6944
$"Unexpected value {token} when parsing the {nameof(HttpMethod)}.");

src/SecTester.Repeater/Internal/MessagePackStringEnumMemberFormatter.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@ internal class MessagePackStringEnumMemberFormatter<T> : IMessagePackFormatter<T
2020
})
2121
.ToDictionary(x => x.Value, x => x.StringValue);
2222

23-
private readonly Dictionary<string, T> CasedStringToEnum;
24-
private readonly Dictionary<T, string> CasedEnumToString;
23+
private readonly Dictionary<string, T> _casedStringToEnum;
24+
private readonly Dictionary<T, string> _casedEnumToString;
2525

2626
public MessagePackStringEnumMemberFormatter(MessagePackNamingPolicy namingPolicy)
2727
{
28-
this.CasedEnumToString = EnumToString.ToDictionary(x => x.Key, x => namingPolicy.ConvertName(x.Value));
29-
this.CasedStringToEnum = EnumToString.ToDictionary(x => namingPolicy.ConvertName(x.Value), x => x.Key);
28+
this._casedEnumToString = EnumToString.ToDictionary(x => x.Key, x => namingPolicy.ConvertName(x.Value));
29+
this._casedStringToEnum = EnumToString.ToDictionary(x => namingPolicy.ConvertName(x.Value), x => x.Key);
3030
}
3131

3232
public void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options)
3333
{
34-
if (!CasedEnumToString.TryGetValue(value, out var stringValue))
34+
if (!_casedEnumToString.TryGetValue(value, out var stringValue))
3535
{
3636
throw new MessagePackSerializationException($"No string representation found for {value}");
3737
}
@@ -43,7 +43,7 @@ public T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions
4343
{
4444
var stringValue = reader.ReadString();
4545

46-
if (null == stringValue || !CasedStringToEnum.TryGetValue(stringValue, out var enumValue))
46+
if (null == stringValue || !_casedStringToEnum.TryGetValue(stringValue, out var enumValue))
4747
{
4848
throw new MessagePackSerializationException($"Unable to parse '{stringValue}' to {typeof(T).Name}.");
4949
}

src/SecTester.Repeater/SecTester.Repeater.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
<ItemGroup>
1919
<Folder Include="Api"/>
20+
<Folder Include="Bus" />
2021
<Folder Include="Extensions"/>
22+
<Folder Include="Internal"/>
2123
<Folder Include="Runners"/>
2224
</ItemGroup>
2325

test/SecTester.Repeater.Tests/Bus/IncomingRequestTests.cs

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public void IncomingRequest_FromDictionary_ShouldCreateInstance(IncomingRequest
5656
}
5757

5858
[Fact]
59-
public void IncomingRequest_FromDictionary_ShouldThrowWhenRequiredPropertyWasNotProvided()
59+
public void IncomingRequest_FromDictionary_ShouldThrowWhenProtocolIsInvalid()
6060
{
6161
// arrange
6262
var packJson =
@@ -72,7 +72,7 @@ public void IncomingRequest_FromDictionary_ShouldThrowWhenRequiredPropertyWasNot
7272
var act = () => IncomingRequest.FromDictionary(deserializedDictionary);
7373

7474
// assert
75-
act.Should().Throw<InvalidDataException>();
75+
act.Should().Throw<ArgumentException>();
7676
}
7777

7878
[Fact]
@@ -114,24 +114,4 @@ public void IncomingRequest_FromDictionary_ShouldParseProtocolValue()
114114
// assert
115115
result.Should().BeEquivalentTo(new IncomingRequest(new Uri("https://foo.bar/1")) { Protocol = Protocol.Http });
116116
}
117-
118-
[Fact]
119-
public void IncomingRequest_FromDictionary_ShouldThrowWhenProtocolIsNotParsable()
120-
{
121-
// arrange
122-
var packJson =
123-
"{\"type\":2,\"data\":[\"request\",{\"protocol\":\"ws\",\"url\":\"https://foo.bar/1\"}],\"options\":{\"compress\":true},\"id\":1,\"nsp\":\"/some\"}";
124-
125-
var serializer = new SocketIOMessagePackSerializer(Options);
126-
127-
var deserializedPackMessage = MessagePackSerializer.Deserialize<PackMessage>(MessagePackSerializer.ConvertFromJson(packJson), Options);
128-
129-
var deserializedDictionary = serializer.Deserialize<Dictionary<object, object>>(deserializedPackMessage, 1);
130-
131-
// act
132-
var act = () => IncomingRequest.FromDictionary(deserializedDictionary);
133-
134-
// assert
135-
act.Should().Throw<InvalidDataException>();
136-
}
137117
}

test/SecTester.Repeater.Tests/Internal/MessagePackHttpHeadersFormatterTests.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,23 @@ public sealed class MessagePackHttpHeadersFormatterTests
1313
)
1414
);
1515

16-
public static readonly IEnumerable<object[]> Fixtures = new List<object[]>()
16+
public static readonly IEnumerable<object?[]> Fixtures = new List<object?[]>()
1717
{
18-
new object[]
18+
new object?[]
1919
{
2020
null
2121
},
2222
new object[]
2323
{
2424
Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>()
25-
2625
},
27-
new object[]
26+
new object?[]
2827
{
2928
new List<KeyValuePair<string, IEnumerable<string>>>
3029
{
3130
new("content-type", new List<string> { "application/json" }),
3231
new("cache-control", new List<string> { "no-cache", "no-store" })
33-
}.AsEnumerable()
32+
}
3433
}
3534
};
3635

0 commit comments

Comments
 (0)