Permalink
Browse files

Extracted request dispatcher from service.

Introduced request/response features.
Introduced request/response pipeline.
Always returns a resource (instead of just a response when an error occurs etc).
Refactoring of unit tests and removal of redundant tests.
  • Loading branch information...
1 parent 208ebd8 commit 3049c09be179f25c7d34051a7f098f8dbf8bde03 @MrBretticus MrBretticus committed Dec 21, 2011
Showing with 5,928 additions and 545 deletions.
  1. +2 −0 .gitignore
  2. +11 −1 RestfulieClient/RestfulieClient.csproj
  3. +11 −0 RestfulieClient/features/IRequestFeature.cs
  4. +10 −0 RestfulieClient/features/IResponseFeature.cs
  5. +56 −0 RestfulieClient/http/DefaultRequestDispatcher.cs
  6. +18 −0 RestfulieClient/request/IRequestDispatcher.cs
  7. +33 −0 RestfulieClient/request/Request.cs
  8. +20 −0 RestfulieClient/request/RequestChain.cs
  9. +37 −0 RestfulieClient/request/RequestStack.cs
  10. +33 −0 RestfulieClient/request/ResponseChain.cs
  11. +31 −24 RestfulieClient/resources/DynamicXmlResource.cs
  12. +24 −0 RestfulieClient/resources/EmptyResource.cs
  13. +89 −101 RestfulieClient/resources/EntryPointService.cs
  14. +30 −0 RestfulieClient/resources/IResource.cs
  15. +2 −1 RestfulieClient/resources/{Restifulie.cs → Restfulie.cs}
  16. +11 −7 RestfulieClient/service/HttpRemoteResponse.cs
  17. +58 −6 RestfulieClient/service/IRemoteResourceService.cs
  18. +3 −2 RestfulieClient/service/RestfulieHttpVerbDiscovery.cs
  19. +81 −0 RestfulieClientTest/BaseTest.cs
  20. +11 −94 RestfulieClientTest/DynamicXmlResourceTest.cs
  21. +139 −0 RestfulieClientTest/EntryPointServiceTests.cs
  22. +8 −66 RestfulieClientTest/EntryPointTests.cs
  23. +0 −92 RestfulieClientTest/ResourceServiceTest.cs
  24. +5 −16 RestfulieClientTest/RestfulieClientTests.csproj
  25. +12 −56 RestfulieClientTest/RestfulieHttpVerbDiscoveryTest.cs
  26. +2 −54 RestfulieClientTest/StringValueConverterTest.cs
  27. +57 −0 RestfulieClientTest/helpers/EmbeddedFileResourceDispatcher.cs
  28. +14 −20 RestfulieClientTest/helpers/LoadDocument.cs
  29. +0 −5 RestfulieClientTest/helpers/RemoteResourceFactory.cs
  30. BIN libs/Moq-4.0/Moq.dll
  31. +5,120 −0 libs/Moq-4.0/Moq.xml
View
@@ -25,3 +25,5 @@
_ReSharper.*/
Thumbs.db
*.exe
+
+/TestResults
@@ -40,9 +40,19 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="features\IRequestFeature.cs" />
+ <Compile Include="features\IResponseFeature.cs" />
+ <Compile Include="http\DefaultRequestDispatcher.cs" />
+ <Compile Include="request\IRequestDispatcher.cs" />
+ <Compile Include="request\Request.cs" />
+ <Compile Include="request\RequestChain.cs" />
+ <Compile Include="request\RequestStack.cs" />
+ <Compile Include="request\ResponseChain.cs" />
<Compile Include="resources\DynamicXmlResource.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="resources\Restifulie.cs" />
+ <Compile Include="resources\EmptyResource.cs" />
+ <Compile Include="resources\IResource.cs" />
+ <Compile Include="resources\Restfulie.cs" />
<Compile Include="resources\EntryPointService.cs" />
<Compile Include="service\HttpRemoteResponseFactory.cs" />
<Compile Include="service\HttpRemoteResponse.cs" />
@@ -0,0 +1,11 @@
+using System;
+using RestfulieClient.request;
+using RestfulieClient.service;
+
+namespace RestfulieClient.features
+{
+ public interface IRequestFeature
+ {
+ HttpRemoteResponse Process(RequestChain chain, Request request, string verb, Uri uri, string content);
+ }
+}
@@ -0,0 +1,10 @@
+using RestfulieClient.request;
+using RestfulieClient.service;
+
+namespace RestfulieClient.features
+{
+ public interface IResponseFeature
+ {
+ HttpRemoteResponse Process(ResponseChain chain, HttpRemoteResponse response);
+ }
+}
@@ -0,0 +1,56 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using RestfulieClient.request;
+using RestfulieClient.service;
+
+namespace RestfulieClient.http
+{
+ public class DefaultRequestDispatcher : IRequestDispatcher
+ {
+ private string GetContent(HttpWebResponse response) {
+ Stream stream = response.GetResponseStream();
+
+ if (stream == null || stream.Length == 0)
+ return null;
+
+ using (var reader = new StreamReader(stream))
+ return reader.ReadToEnd();
+ }
+
+ private void WriteContent(HttpWebRequest request, string content) {
+ byte[] byteArray = Encoding.UTF8.GetBytes(content);
+ request.ContentLength = byteArray.Length;
+ Stream bodyStream = request.GetRequestStream();
+ bodyStream.Write(byteArray, 0, byteArray.Length);
+ bodyStream.Close();
+ }
+
+ private HttpRemoteResponse GetRemoteResponse(HttpWebResponse response) {
+ return new HttpRemoteResponse(
+ response.StatusCode,
+ response.Headers.AllKeys.ToDictionary(k => k, k => response.Headers[k], StringComparer.OrdinalIgnoreCase),
+ GetContent(response));
+ }
+
+ public HttpRemoteResponse Process(IRemoteResourceService service, string verb, Uri uri, string content) {
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
+ try
+ {
+ foreach (var header in service.Headers)
+ request.Headers.Add(header.Key, header.Value);
+
+ request.Method = verb;
+ if (!String.IsNullOrWhiteSpace(content))
+ WriteContent(request, content);
+ return GetRemoteResponse((HttpWebResponse)request.GetResponse());
+ }
+ catch (Exception ex)
+ {
+ throw new ArgumentException(string.Format("An error occurred while connecting to the resource in url {0} with message {1}.", uri, ex.Message), ex);
+ }
+ }
+ }
+}
@@ -0,0 +1,18 @@
+using System;
+using RestfulieClient.service;
+
+namespace RestfulieClient.request
+{
+ public interface IRequestDispatcher
+ {
+ /// <summary>
+ /// Instructs the dispatcher to process the given request based on it's current configuration
+ /// </summary>
+ /// <param name="service">The service that called the dispatcher</param>
+ /// <param name="verb">The HTTP verb to use for making the request</param>
+ /// <param name="uri">The URI to use for making the request</param>
+ /// <param name="content">The text to include in the request body</param>
+ /// <returns>The result of the request (including status code and content), will be null if using an asynch callback</returns>
+ HttpRemoteResponse Process(IRemoteResourceService service, string verb, Uri uri, string content);
+ }
+}
@@ -0,0 +1,33 @@
+using System;
+using RestfulieClient.features;
+using RestfulieClient.service;
+
+namespace RestfulieClient.request
+{
+ public class Request
+ {
+ private readonly IRequestDispatcher _dispatcher;
+ private readonly RequestStack _stack;
+
+ public IRequestDispatcher Dispatcher {
+ get { return _dispatcher; }
+ }
+
+ public Request(IRemoteResourceService service, IRequestDispatcher dispatcher) {
+ _dispatcher = dispatcher;
+ _stack = new RequestStack(service);
+ }
+
+ public void AddFeature(IRequestFeature requestFeature) {
+ _stack.AddFeature(requestFeature);
+ }
+
+ public void AddFeature(IResponseFeature responseFeature) {
+ _stack.AddFeature(responseFeature);
+ }
+
+ public virtual HttpRemoteResponse Process(Uri uri, string verb, string content) {
+ return _stack.Process(this, verb, uri, content);
+ }
+ }
+}
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using RestfulieClient.features;
+using RestfulieClient.service;
+
+namespace RestfulieClient.request
+{
+ public class RequestChain
+ {
+ private readonly IEnumerator<IRequestFeature> _current;
+
+ public RequestChain(IEnumerable<IRequestFeature> features) {
+ _current = features.GetEnumerator();
+ }
+
+ public HttpRemoteResponse Next(Request request, string verb, Uri uri, string content) {
+ return _current.MoveNext() ? _current.Current.Process(this, request, verb, uri, content) : null;
+ }
+ }
+}
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using RestfulieClient.features;
+using RestfulieClient.service;
+
+namespace RestfulieClient.request
+{
+ public class RequestStack : IRequestFeature
+ {
+ private readonly IRemoteResourceService _service;
+ private readonly IList<IRequestFeature> _requestFeatures = new List<IRequestFeature>();
+ private readonly IList<IResponseFeature> _responseFeatures = new List<IResponseFeature>();
+
+ public RequestStack(IRemoteResourceService service) {
+ _service = service;
+ }
+
+ public void AddFeature(IRequestFeature requestFeature) {
+ _requestFeatures.Add(requestFeature);
+ }
+
+ public void AddFeature(IResponseFeature responseFeature) {
+ _responseFeatures.Add(responseFeature);
+ }
+
+ public HttpRemoteResponse Process(Request request, string verb, Uri uri, string content) {
+ _requestFeatures.Add(this);
+
+ return new RequestChain(_requestFeatures).Next(request, verb, uri, content);
+ }
+
+ public virtual HttpRemoteResponse Process(RequestChain chain, Request request, string verb, Uri uri, string content) {
+ return new ResponseChain(_service, _responseFeatures)
+ .Next(_service.Dispatcher.Process(_service, verb, uri, content));
+ }
+ }
+}
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RestfulieClient.features;
+using RestfulieClient.resources;
+using RestfulieClient.service;
+
+namespace RestfulieClient.request
+{
+ public class ResponseChain
+ {
+ private readonly IRemoteResourceService _service;
+ private readonly IEnumerator<IResponseFeature> _current;
+
+ public IRemoteResourceService Service {
+ get { return _service; }
+ }
+
+ public ResponseChain(IRemoteResourceService service, IEnumerable<IResponseFeature> features) {
+ _service = service;
+ _current = features.GetEnumerator();
+ }
+
+ public void Reset() {
+ _current.Reset();
+ }
+
+ public HttpRemoteResponse Next(HttpRemoteResponse response) {
+ return _current.MoveNext() ? _current.Current.Process(this, response) : response;
+ }
+ }
+}
@@ -2,62 +2,66 @@
using System.Linq;
using System.Xml.Linq;
using System.Dynamic;
-using System.Reflection;
using RestfulieClient.service;
using System.Globalization;
namespace RestfulieClient.resources
{
- public class DynamicXmlResource : DynamicObject
+ public class DynamicXmlResource : DynamicObject, IResource
{
- private StringValueConverter converter = new StringValueConverter();
+ private readonly StringValueConverter _converter = new StringValueConverter();
public HttpRemoteResponse WebResponse { get; private set; }
+
+ public bool IsEmpty {
+ get { return WebResponse.HasNoContent(); } // should check if any nodes exist
+ }
+
public IRemoteResourceService RemoteResourceService { get; private set; }
public NumberFormatInfo NumberFormatInfo { get; set; }
public XElement XmlRepresentation
{
get
{
- if (this.WebResponse.HasNoContent())
+ if (WebResponse.HasNoContent())
return null;
- else
- return XElement.Parse(this.WebResponse.Content);
+
+ return XElement.Parse(WebResponse.Content);
}
}
public DynamicXmlResource(HttpRemoteResponse response)
{
- this.WebResponse = response;
- this.NumberFormatInfo = System.Globalization.NumberFormatInfo.CurrentInfo;
+ WebResponse = response;
+ NumberFormatInfo = NumberFormatInfo.CurrentInfo;
}
public DynamicXmlResource(HttpRemoteResponse response, IRemoteResourceService remoteService)
: this(response)
{
- this.RemoteResourceService = remoteService;
+ RemoteResourceService = remoteService;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string fieldName = binder.Name.Replace("_", "-").ToLower();
- XElement firstElement = this.GetFirstElementWithName(fieldName);
- result = this.GetValueFromXmlElement(firstElement);
+ XElement firstElement = GetFirstElementWithName(fieldName);
+ result = GetValueFromXmlElement(firstElement);
return result != null ? true : false;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
- object value = this.GetValueFromAttributeName(binder.Name, "href");
+ object value = GetValueFromAttributeName(binder.Name, "href");
if (value == null)
throw new ArgumentException(string.Format("There is not method defined with name:", binder.Name));
- DynamicXmlResource resource = (DynamicXmlResource)this.InvokeRemoteResource(value.ToString(), binder.Name);
+ DynamicXmlResource resource = (DynamicXmlResource)InvokeRemoteResource(value.ToString(), binder.Name);
if (resource.WebResponse.HasNoContent())
{
- result = this.XmlRepresentation;
- this.UpdateWebResponse(resource.WebResponse);
+ result = XmlRepresentation;
+ UpdateWebResponse(resource.WebResponse);
}
else
{
@@ -70,12 +74,7 @@ private object InvokeRemoteResource(string url, string transitionName)
{
try
{
- Type remoteResourceServiceType = this.RemoteResourceService.GetType();
- return remoteResourceServiceType.InvokeMember("Execute",
- BindingFlags.InvokeMethod |
- BindingFlags.Public |
- BindingFlags.Instance,
- null, this.RemoteResourceService, new Object[] { url, transitionName });
+ return RemoteResourceService.Execute(url, transitionName);
}
catch (Exception ex)
{
@@ -89,11 +88,11 @@ private object GetValueFromXmlElement(XElement element)
{
if (element.HasElements)
{
- return new DynamicXmlResource(this.WebResponse);
+ return new DynamicXmlResource(WebResponse);
}
else
{
- object result = this.converter.TransformText(element.Value).WithNumberFormatInfo(this.NumberFormatInfo).ToValue();
+ object result = _converter.TransformText(element.Value).WithNumberFormatInfo(NumberFormatInfo).ToValue();
return result;
}
}
@@ -123,7 +122,15 @@ private object GetValueFromAttributeName(string name, string attributeName)
private void UpdateWebResponse(HttpRemoteResponse response)
{
- this.WebResponse = response;
+ WebResponse = response;
+ }
+
+ public bool HasLink(string rel) {
+ throw new NotImplementedException();
+ }
+
+ public IResource Follow(string rel, string content) {
+ throw new NotImplementedException();
}
}
}
Oops, something went wrong.

0 comments on commit 3049c09

Please sign in to comment.