Skip to content

Commit 5629d2b

Browse files
authored
Drop usage of reflection (#113)
1 parent 2733be0 commit 5629d2b

5 files changed

Lines changed: 134 additions & 77 deletions

File tree

Source/EasyNetQ.Management.Client.Tests/EasyNetQ.Management.Client.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFrameworks>netcoreapp2.0;net452</TargetFrameworks>

Source/EasyNetQ.Management.Client/ManagementClient.cs

Lines changed: 96 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Net;
44
using System.Net.Http;
55
using System.Net.Http.Headers;
6-
using System.Reflection;
76
using System.Security;
87
using System.Text;
98
using System.Text.RegularExpressions;
@@ -18,6 +17,8 @@ namespace EasyNetQ.Management.Client
1817
{
1918
public class ManagementClient : IManagementClient
2019
{
20+
private static readonly Regex ParameterNameRegex = new Regex("([a-z])([A-Z])", RegexOptions.Compiled);
21+
2122
private static Task CompletedTask { get; } = Task.FromResult<object>(null);
2223

2324
private static readonly MediaTypeWithQualityHeaderValue JsonMediaTypeHeaderValue =
@@ -94,22 +95,27 @@ public ManagementClient(
9495
throw new ArgumentException("hostUrl is illegal");
9596
hostUrl = hostUrl.Contains("http://") ? hostUrl : "http://" + hostUrl;
9697
}
98+
9799
if (!urlRegex.IsMatch(hostUrl) || !Uri.TryCreate(hostUrl, UriKind.Absolute, out var urlUri))
98100
{
99101
throw new ArgumentException("hostUrl is illegal");
100102
}
103+
101104
if (string.IsNullOrEmpty(username))
102105
{
103106
throw new ArgumentException("username is null or empty");
104107
}
108+
105109
if (password == null || password.Length == 0)
106110
{
107111
throw new ArgumentException("password is null or empty");
108112
}
113+
109114
if (configureRequest == null)
110115
{
111116
configureRequest = x => { };
112117
}
118+
113119
HostUrl = hostUrl;
114120
Username = username;
115121
PortNumber = portNumber;
@@ -131,7 +137,11 @@ public ManagementClient(
131137
public Task<Overview> GetOverviewAsync(GetLengthsCriteria lengthsCriteria = null,
132138
GetRatesCriteria ratesCriteria = null, CancellationToken cancellationToken = default(CancellationToken))
133139
{
134-
return GetAsync<Overview>("overview", cancellationToken, lengthsCriteria, ratesCriteria);
140+
var queryParameters = MergeQueryParameters(
141+
lengthsCriteria?.ToQueryParameters(),
142+
ratesCriteria?.ToQueryParameters()
143+
);
144+
return GetAsync<Overview>("overview", queryParameters, cancellationToken);
135145
}
136146

137147
public Task<IEnumerable<Node>> GetNodesAsync(CancellationToken cancellationToken = default(CancellationToken))
@@ -168,7 +178,7 @@ public Task<Channel> GetChannelAsync(string channelName, GetRatesCriteria ratesC
168178
{
169179
Ensure.ArgumentNotNull(channelName, nameof(channelName));
170180

171-
return GetAsync<Channel>($"channels/{channelName}", cancellationToken, ratesCriteria);
181+
return GetAsync<Channel>($"channels/{channelName}", ratesCriteria?.ToQueryParameters(), cancellationToken);
172182
}
173183

174184
public Task<IEnumerable<Exchange>> GetExchangesAsync(
@@ -183,18 +193,21 @@ public Task<Exchange> GetExchangeAsync(string exchangeName, Vhost vhost, GetRate
183193
Ensure.ArgumentNotNull(exchangeName, nameof(exchangeName));
184194
Ensure.ArgumentNotNull(vhost, nameof(vhost));
185195

186-
return GetAsync<Exchange>($"exchanges/{SanitiseVhostName(vhost.Name)}/{exchangeName}", cancellationToken,
187-
ratesCriteria);
196+
return GetAsync<Exchange>($"exchanges/{SanitiseVhostName(vhost.Name)}/{exchangeName}", ratesCriteria?.ToQueryParameters(), cancellationToken);
188197
}
189198

190199
public Task<Queue> GetQueueAsync(string queueName, Vhost vhost, GetLengthsCriteria lengthsCriteria = null,
191200
GetRatesCriteria ratesCriteria = null, CancellationToken cancellationToken = default(CancellationToken))
192201
{
193202
Ensure.ArgumentNotNull(queueName, nameof(queueName));
194203
Ensure.ArgumentNotNull(vhost, nameof(vhost));
195-
204+
205+
var queryParameters = MergeQueryParameters(
206+
lengthsCriteria?.ToQueryParameters(),
207+
ratesCriteria?.ToQueryParameters()
208+
);
196209
return GetAsync<Queue>($"queues/{SanitiseVhostName(vhost.Name)}/{SanitiseName(queueName)}",
197-
cancellationToken, lengthsCriteria, ratesCriteria);
210+
queryParameters, cancellationToken);
198211
}
199212

200213
public async Task<Exchange> CreateExchangeAsync(ExchangeInfo exchangeInfo, Vhost vhost,
@@ -363,12 +376,12 @@ public Task DeleteBindingAsync(Binding binding,
363376
{
364377
throw new ArgumentException("Empty binding source isn't supported.");
365378
}
366-
379+
367380
if (string.IsNullOrEmpty(binding.Destination))
368381
{
369382
throw new ArgumentException("Empty binding destination isn't supported.");
370383
}
371-
384+
372385
if (string.IsNullOrEmpty(binding.DestinationType))
373386
{
374387
throw new ArgumentException("Empty binding destination type isn't supported.");
@@ -452,10 +465,12 @@ public Task<IEnumerable<Policy>> GetPoliciesAsync(
452465
{
453466
throw new ArgumentException("Policy name is empty");
454467
}
468+
455469
if (string.IsNullOrEmpty(policy.Vhost))
456470
{
457471
throw new ArgumentException("vhost name is empty");
458472
}
473+
459474
if (policy.Definition == null)
460475
{
461476
throw new ArgumentException("Definition should not be null");
@@ -487,14 +502,14 @@ public Task CreateParameterAsync(Parameter parameter,
487502
cancellationToken);
488503
}
489504

490-
public async Task DeleteParameterAsync(string componentName, string vhost, string name,
505+
public Task DeleteParameterAsync(string componentName, string vhost, string name,
491506
CancellationToken cancellationToken = default(CancellationToken))
492507
{
493508
Ensure.ArgumentNotNull(componentName, nameof(componentName));
494509
Ensure.ArgumentNotNull(vhost, nameof(vhost));
495510
Ensure.ArgumentNotNull(name, nameof(name));
496511

497-
await DeleteAsync(GetParameterUrl(componentName, vhost, name), cancellationToken).ConfigureAwait(false);
512+
return DeleteAsync(GetParameterUrl(componentName, vhost, name), cancellationToken);
498513
}
499514

500515
public async Task<User> CreateUserAsync(UserInfo userInfo,
@@ -574,6 +589,7 @@ public async Task<User> ChangeUserPasswordAsync(string userName, string newPassw
574589
{
575590
userInfo.AddTag(tag.Trim());
576591
}
592+
577593
return await CreateUserAsync(userInfo, cancellationToken).ConfigureAwait(false);
578594
}
579595

@@ -588,35 +604,40 @@ public async Task<bool> IsAliveAsync(Vhost vhost,
588604
{
589605
Ensure.ArgumentNotNull(vhost, nameof(vhost));
590606

591-
var result =
592-
await GetAsync<AlivenessTestResult>($"aliveness-test/{SanitiseVhostName(vhost.Name)}",
593-
cancellationToken).ConfigureAwait(false);
607+
var result = await GetAsync<AlivenessTestResult>($"aliveness-test/{SanitiseVhostName(vhost.Name)}",
608+
cancellationToken).ConfigureAwait(false);
594609
return result.Status == "ok";
595610
}
596611

597612
private Task<T> GetAsync<T>(
598613
string path,
599-
CancellationToken cancellationToken = default(CancellationToken),
600-
params object[] queryObjects)
614+
CancellationToken cancellationToken = default(CancellationToken))
601615
{
602-
var request = CreateRequestForPath(path, HttpMethod.Get, queryObjects);
616+
return GetAsync<T>(path, null, cancellationToken);
617+
}
618+
603619

620+
private Task<T> GetAsync<T>(
621+
string path,
622+
IReadOnlyDictionary<string, string> queryParameters,
623+
CancellationToken cancellationToken = default(CancellationToken))
624+
{
625+
var request = CreateRequestForPath(HttpMethod.Get, path, BuildQueryString(queryParameters));
604626
return httpClient.SendAsync(request, cancellationToken)
605627
.ContinueWithOrThrow(_ => AnalyseResponse<T>(code => code == HttpStatusCode.OK, _.Result), cancellationToken)
606-
.ContinueWith(__ =>
607-
{
608-
request?.Dispose();
609-
return __;
610-
}, cancellationToken).Unwrap();
611-
628+
.ContinueWith(__ =>
629+
{
630+
request?.Dispose();
631+
return __;
632+
}, cancellationToken).Unwrap();
612633
}
613634

614635
private Task<TResult> PostAsync<TItem, TResult>(
615636
string path,
616637
TItem item,
617638
CancellationToken cancellationToken = default(CancellationToken))
618639
{
619-
var request = CreateRequestForPath(path, HttpMethod.Post);
640+
var request = CreateRequestForPath(HttpMethod.Post, path, string.Empty);
620641

621642
InsertRequestBody(request, item);
622643

@@ -633,35 +654,33 @@ bool Success(HttpStatusCode statusCode) =>
633654
request?.Dispose();
634655
return __;
635656
}, cancellationToken);
636-
637-
},cancellationToken).Unwrap();
657+
}, cancellationToken).Unwrap();
638658
}
639659

640660
private Task DeleteAsync(
641661
string path,
642662
CancellationToken cancellationToken = default(CancellationToken))
643663
{
644-
var request = CreateRequestForPath(path, HttpMethod.Delete);
645-
664+
var request = CreateRequestForPath(HttpMethod.Delete, path, string.Empty);
665+
646666
return httpClient.SendAsync(request, cancellationToken)
647667
.ContinueWithOrThrow(_ =>
648-
AnalyseResponse(statusCode => statusCode == HttpStatusCode.NoContent, _.Result)
649-
.ContinueWith(__ =>
650-
{
651-
request?.Dispose();
652-
return __;
653-
}, cancellationToken).Unwrap()
668+
AnalyseResponse(statusCode => statusCode == HttpStatusCode.NoContent, _.Result)
669+
.ContinueWith(__ =>
670+
{
671+
request?.Dispose();
672+
return __;
673+
}, cancellationToken).Unwrap()
654674
, cancellationToken);
655-
656675
}
657676

658677
private Task PutAsync<T>(
659678
string path,
660679
T item = default(T),
661680
CancellationToken cancellationToken = default(CancellationToken)) where T : class
662681
{
663-
var request = CreateRequestForPath(path, HttpMethod.Put);
664-
682+
var request = CreateRequestForPath(HttpMethod.Put, path, string.Empty);
683+
665684
if (item != default(T))
666685
InsertRequestBody(request, item);
667686

@@ -673,16 +692,15 @@ bool ResponseSucceeded(HttpStatusCode statusCode) => statusCode == HttpStatusCod
673692
statusCode == HttpStatusCode.NoContent;
674693

675694
return AnalyseResponse(ResponseSucceeded, _.Result)
676-
.ContinueWith(__ =>
677-
{
678-
request?.Dispose();
679-
return __;
680-
}, cancellationToken).Unwrap();
695+
.ContinueWith(__ =>
696+
{
697+
request?.Dispose();
698+
return __;
699+
}, cancellationToken).Unwrap();
681700
}, cancellationToken);
682-
683701
}
684702

685-
private Task AnalyseResponse(
703+
private static Task AnalyseResponse(
686704
Func<HttpStatusCode, bool> success,
687705
HttpResponseMessage responseMessage)
688706
{
@@ -699,15 +717,15 @@ private Task AnalyseResponse(
699717
}
700718
}
701719

702-
private static Task<TResult> AnalyseResponse<TResult>(
720+
private static Task<T> AnalyseResponse<T>(
703721
Func<HttpStatusCode, bool> success,
704722
HttpResponseMessage responseMessage)
705723
{
706724
var httpStatusCode = responseMessage.StatusCode;
707725
try
708726
{
709727
if (success(httpStatusCode))
710-
return DeserializeResponseAsync<TResult>(responseMessage);
728+
return DeserializeResponseAsync<T>(responseMessage);
711729
throw new UnexpectedHttpStatusCodeException(httpStatusCode);
712730
}
713731
finally
@@ -744,44 +762,31 @@ private static Task<T> DeserializeResponseAsync<T>(HttpResponseMessage response)
744762
.ContinueWith(_ => JsonConvert.DeserializeObject<T>(_.Result, Settings));
745763
}
746764

747-
private HttpRequestMessage CreateRequestForPath(string path, HttpMethod httpMethod,
748-
IReadOnlyCollection<object> queryObjects = null)
765+
private HttpRequestMessage CreateRequestForPath(HttpMethod httpMethod, string path, string query)
749766
{
750-
var queryString = BuildQueryString(queryObjects);
751-
752-
var uri = new Uri($"{HostUrl}:{PortNumber}/api/{path}{queryString}");
767+
var uri = new Uri($"{HostUrl}:{PortNumber}/api/{path}{query ?? string.Empty}");
753768
var request = new HttpRequestMessage(httpMethod, uri);
754-
755769
configureRequest(request);
756-
757770
return request;
758771
}
759772

760-
// Very simple query-string builder.
761-
private static string BuildQueryString(IReadOnlyCollection<object> queryObjects)
773+
private static string BuildQueryString(IReadOnlyDictionary<string, string> queryParameters)
762774
{
763-
if (queryObjects == null || queryObjects.Count == 0)
775+
if (queryParameters == null || queryParameters.Count == 0)
764776
return string.Empty;
765777

766778
var queryStringBuilder = new StringBuilder("?");
767779
var first = true;
768-
// One or more query objects can be used to build the query
769-
foreach (var query in queryObjects)
780+
foreach (var parameter in queryParameters)
770781
{
771-
if (query == null)
772-
continue;
773-
// All public properties are added to the query on the format property_name=value
774-
var type = query.GetType();
775-
foreach (var prop in type.GetProperties())
776-
{
777-
var name = Regex.Replace(prop.Name, "([a-z])([A-Z])", "$1_$2").ToLower();
778-
var value = prop.GetValue(query, null);
779-
if (!first)
780-
queryStringBuilder.Append("&");
781-
queryStringBuilder.AppendFormat("{0}={1}", name, value ?? string.Empty);
782-
first = false;
783-
}
782+
if (!first)
783+
queryStringBuilder.Append("&");
784+
var name = ParameterNameRegex.Replace(parameter.Key, "$1_$2").ToLower();
785+
var value = parameter.Value ?? "";
786+
queryStringBuilder.AppendFormat("{0}={1}", name, value);
787+
first = false;
784788
}
789+
785790
return queryStringBuilder.ToString();
786791
}
787792

@@ -805,9 +810,27 @@ private static string RecodeBindingPropertiesKey(string propertiesKey)
805810
return propertiesKey.Replace("%5F", "%255F");
806811
}
807812

813+
private static IReadOnlyDictionary<string, string> MergeQueryParameters(params IReadOnlyDictionary<string, string>[] multipleQueryParameters)
814+
{
815+
if (multipleQueryParameters == null || multipleQueryParameters.Length == 0)
816+
return null;
817+
818+
var mergedQueryParameters = new Dictionary<string, string>();
819+
foreach (var queryParameters in multipleQueryParameters)
820+
{
821+
if (queryParameters == null)
822+
continue;
823+
824+
foreach (var kvp in queryParameters)
825+
mergedQueryParameters[kvp.Key] = kvp.Value;
826+
}
827+
828+
return mergedQueryParameters;
829+
}
830+
808831
public void Dispose()
809832
{
810-
httpClient?.Dispose();
833+
httpClient.Dispose();
811834
}
812835
}
813-
}
836+
}

0 commit comments

Comments
 (0)