Skip to content

Commit

Permalink
refactor(repeater): simplify FromDictionary method
Browse files Browse the repository at this point in the history
  • Loading branch information
derevnjuk committed Jun 7, 2024
1 parent fb0478d commit 5260f38
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 84 deletions.
90 changes: 32 additions & 58 deletions src/SecTester.Repeater/Bus/IncomingRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,75 +20,49 @@ public record IncomingRequest(Uri Url) : IRequest
private const string BodyKey = "body";
private const string ProtocolKey = "protocol";

private static readonly Dictionary<string, Protocol> ProtocolEntries = typeof(Protocol)
.GetFields(BindingFlags.Public | BindingFlags.Static)
.Select(field => new
{
Value = (Protocol)field.GetValue(null),
StringValue = field.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? field.Name
})
.ToDictionary(x => MessagePackNamingPolicy.SnakeCase.ConvertName(x.StringValue), x => x.Value);

[Key(ProtocolKey)]
public Protocol Protocol { get; set; } = Protocol.Http;
private static readonly Dictionary<string, Protocol> Protocols = typeof(Protocol)
.GetFields(BindingFlags.Public | BindingFlags.Static)
.Select(field => new
{
Value = (Protocol)field.GetValue(null),
StringValue = field.GetCustomAttribute<EnumMemberAttribute>()?.Value ?? field.Name
})
.ToDictionary(x => MessagePackNamingPolicy.SnakeCase.ConvertName(x.StringValue), x => x.Value);

private IEnumerable<KeyValuePair<string, IEnumerable<string>>> _headers = Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>();
[Key(ProtocolKey)] public Protocol Protocol { get; set; } = Protocol.Http;

[Key(HeadersKey)]
public IEnumerable<KeyValuePair<string, IEnumerable<string>>> Headers
{
get => _headers;
// ADHOC: convert from a kind of assignable type to formatter resolvable type
set => _headers = value.AsEnumerable();
}
[Key(HeadersKey)] public IEnumerable<KeyValuePair<string, IEnumerable<string>>> Headers { get; set; }

[Key(BodyKey)]
public string? Body { get; set; }
[Key(BodyKey)] public string? Body { get; set; }

[Key(MethodKey)]
public HttpMethod Method { get; set; } = HttpMethod.Get;
[Key(MethodKey)] public HttpMethod Method { get; set; } = HttpMethod.Get;

[Key(UrlKey)]
public Uri Url { get; set; } = Url ?? throw new ArgumentNullException(nameof(Url));
[Key(UrlKey)] public Uri Url { get; set; } = Url ?? throw new ArgumentNullException(nameof(Url));

public static IncomingRequest FromDictionary(Dictionary<object, object> dictionary)
{
var protocol = !dictionary.ContainsKey(ProtocolKey) || (dictionary.TryGetValue(ProtocolKey, out var p1) && p1 is null)
? Protocol.Http
: dictionary.TryGetValue(ProtocolKey, out var p2) && p2 is string && ProtocolEntries.TryGetValue(p2.ToString(), out var e)
? e
: throw new InvalidDataException(FormatPropertyError(ProtocolKey));

var uri = dictionary.TryGetValue(UrlKey, out var u) && u is string
? new Uri(u.ToString())
: throw new InvalidDataException(FormatPropertyError(UrlKey));

var method = dictionary.TryGetValue(MethodKey, out var m) && m is string
? new HttpMethod(m.ToString())
: HttpMethod.Get;

var body = dictionary.TryGetValue(BodyKey, out var b) && b is string ? b.ToString() : null;

var headers = dictionary.TryGetValue(HeadersKey, out var h) && h is Dictionary<object, object> value
? MapHeaders(value)
: Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>();

return new IncomingRequest(uri)
var protocol = dictionary.TryGetValue(ProtocolKey, out var p2) ? p2.ToString() : Protocol.Http.ToString();
var uri = dictionary.TryGetValue(UrlKey, out var u) ? u.ToString() : throw PropertyError(UrlKey);
var method = dictionary.TryGetValue(MethodKey, out var m) ? m.ToString() : HttpMethod.Get.ToString();
var body = dictionary.TryGetValue(BodyKey, out var b) ? b.ToString() : null;
var headers = dictionary.TryGetValue(HeadersKey, out var h)
? h as IDictionary<string, object>
: new Dictionary<string, object>();

return new IncomingRequest(new Uri(uri))
{
Protocol = protocol,
Protocol = Protocols.TryGetValue(protocol, out var protocolValue) ? protocolValue : Protocol.Http,
Method = HttpMethods.Items.TryGetValue(method, out var methodValue) ? methodValue : HttpMethod.Get,
Body = body,
Method = method,
Headers = headers
Headers = headers.Select(kvp => kvp.Value switch
{
IEnumerable<object> strings => new KeyValuePair<string, IEnumerable<string>>(kvp.Key.ToString(),
strings.Select(x => x.ToString())),
null => new KeyValuePair<string, IEnumerable<string>>(kvp.Key.ToString(), Enumerable.Empty<string>()),
_ => new KeyValuePair<string, IEnumerable<string>>(kvp.Key.ToString(), new[] { kvp.Value.ToString() })
})
};
}

private static IEnumerable<KeyValuePair<string, IEnumerable<string>>> MapHeaders(Dictionary<object, object> headers) =>
headers.Select(kvp => kvp.Value switch
{
IEnumerable<object> strings => new KeyValuePair<string, IEnumerable<string>>(kvp.Key.ToString(), strings.Select(x => x.ToString())),
null => new KeyValuePair<string, IEnumerable<string>>(kvp.Key.ToString(), Enumerable.Empty<string>()),
_ => new KeyValuePair<string, IEnumerable<string>>(kvp.Key.ToString(), new[] { kvp.Value.ToString() })
});

private static string FormatPropertyError(string propName) => $"{propName} is either null or has an invalid data type or value";
private static InvalidDataException PropertyError(string propName) => new($"{propName} is either null or has an invalid data type or value");
}
30 changes: 30 additions & 0 deletions src/SecTester.Repeater/Internal/HttpMethods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Reflection;

namespace SecTester.Repeater.Internal;

public class HttpMethods
{
public static IDictionary<string, HttpMethod> Items { get; } = typeof(HttpMethod)
.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
.Where(x => x.PropertyType.IsAssignableFrom(typeof(HttpMethod)))
.Select(x => x.GetValue(null))
.Cast<HttpMethod>()
.Concat(new List<HttpMethod>
{
new("PATCH"),
new("COPY"),
new("LINK"),
new("UNLINK"),
new("PURGE"),
new("LOCK"),
new("UNLOCK"),
new("PROPFIND"),
new("VIEW")
})
.Distinct()
.ToDictionary(x => x.Method, x => x, StringComparer.InvariantCultureIgnoreCase);
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using MessagePack;
using MessagePack.Formatters;

namespace SecTester.Repeater.Internal;

internal class MessagePackHttpMethodFormatter : IMessagePackFormatter<HttpMethod?>
{
private static readonly IEnumerable<HttpMethod> BaseMethods = typeof(HttpMethod)
.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
.Where(x => x.PropertyType.IsAssignableFrom(typeof(HttpMethod)))
.Select(x => x.GetValue(null))
.Cast<HttpMethod>();

private static readonly IEnumerable<HttpMethod> CustomMethods = new List<HttpMethod>
{
new("PATCH"),
new("COPY"),
new("LINK"),
new("UNLINK"),
new("PURGE"),
new("LOCK"),
new("UNLOCK"),
new("PROPFIND"),
new("VIEW")
};

private static readonly IDictionary<string, HttpMethod> Methods = BaseMethods.Concat(CustomMethods).Distinct()
.ToDictionary(x => x.Method, x => x, StringComparer.InvariantCultureIgnoreCase);
public void Serialize(ref MessagePackWriter writer, HttpMethod? value, MessagePackSerializerOptions options)
{
if (null == value)
Expand Down Expand Up @@ -63,7 +38,7 @@ public void Serialize(ref MessagePackWriter writer, HttpMethod? value, MessagePa
{
var token = reader.ReadString();

if (token is null || !Methods.TryGetValue(token, out var method))
if (token is null || !HttpMethods.Items.TryGetValue(token, out var method))
{
throw new MessagePackSerializationException(
$"Unexpected value {token} when parsing the {nameof(HttpMethod)}.");
Expand Down
2 changes: 2 additions & 0 deletions src/SecTester.Repeater/SecTester.Repeater.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

<ItemGroup>
<Folder Include="Api"/>
<Folder Include="Bus" />
<Folder Include="Extensions"/>
<Folder Include="Internal"/>
<Folder Include="Runners"/>
</ItemGroup>

Expand Down

0 comments on commit 5260f38

Please sign in to comment.