diff --git a/ButterCMS/ButterCMS.csproj b/ButterCMS/ButterCMS.csproj
index d1fdedf..aae1ef0 100644
--- a/ButterCMS/ButterCMS.csproj
+++ b/ButterCMS/ButterCMS.csproj
@@ -60,6 +60,9 @@
+
+
+
diff --git a/ButterCMS/ButterCMSClient.cs b/ButterCMS/ButterCMSClient.cs
index 38350e3..27c1f43 100644
--- a/ButterCMS/ButterCMSClient.cs
+++ b/ButterCMS/ButterCMSClient.cs
@@ -12,9 +12,12 @@ namespace ButterCMS
{
public class ButterCMSClient
{
+ private static readonly string generalExMsg = "There is a problem with the ButterCMS service";
+
private string authToken;
private HttpClient httpClient;
private TimeSpan defaultTimeout = new TimeSpan(0, 0, 10);
+ private int maxRequestTries;
private const string apiBaseAddress = "https://api.buttercms.com/";
@@ -41,13 +44,14 @@ private string authTokenParam
}
private JsonSerializerSettings serializerSettings;
- public ButterCMSClient(string authToken, TimeSpan? timeOut = null)
+ public ButterCMSClient(string authToken, TimeSpan? timeOut = null, int maxRequestTries = 3)
{
httpClient = new HttpClient
{
Timeout = timeOut ?? defaultTimeout,
BaseAddress = new Uri(apiBaseAddress)
};
+ this.maxRequestTries = maxRequestTries;
this.authToken = authToken;
serializerSettings = new JsonSerializerSettings();
@@ -453,8 +457,30 @@ public async Task RetrieveContentFieldsJSONAsync(string[] keys)
var contentFields = await ExecuteAsync(queryString);
return contentFields;
}
-
+
private string Execute(string queryString)
+ {
+ var remainingTries = maxRequestTries;
+ var exceptions = new List();
+
+ do
+ {
+ --remainingTries;
+ try
+ {
+ return ExecuteSingle(queryString);
+ }
+ catch (Exception e)
+ {
+ exceptions.Add(e);
+ }
+ }
+ while (remainingTries > 0);
+
+ throw aggregateExceptions(exceptions);
+ }
+
+ private string ExecuteSingle(string queryString)
{
try
{
@@ -469,7 +495,7 @@ private string Execute(string queryString)
}
if (response.StatusCode >= System.Net.HttpStatusCode.InternalServerError)
{
- throw new Exception("There is a problem with the ButterCMS service");
+ throw new Exception(generalExMsg);
}
}
catch (TaskCanceledException taskException)
@@ -492,6 +518,28 @@ private string Execute(string queryString)
}
private async Task ExecuteAsync(string queryString)
+ {
+ var remainingTries = maxRequestTries;
+ var exceptions = new List();
+
+ do
+ {
+ --remainingTries;
+ try
+ {
+ return await ExecuteSingleAsync(queryString);
+ }
+ catch (Exception e)
+ {
+ exceptions.Add(e);
+ }
+ }
+ while (remainingTries > 0);
+
+ throw aggregateExceptions(exceptions);
+ }
+
+ private async Task ExecuteSingleAsync(string queryString)
{
try
{
@@ -506,7 +554,7 @@ private async Task ExecuteAsync(string queryString)
}
if (response.StatusCode >= System.Net.HttpStatusCode.InternalServerError)
{
- throw new Exception("There is a problem with the ButterCMS service");
+ throw new Exception(generalExMsg);
}
}
catch (TaskCanceledException taskException)
@@ -527,5 +575,55 @@ private async Task ExecuteAsync(string queryString)
}
return string.Empty;
}
+
+ private Exception aggregateExceptions(List exceptions)
+ {
+ // If we somehow managed to fail all the requests without getting any exceptions,
+ // return a general exception. This shouldn't be possible.
+ if (!exceptions.Any())
+ return new Exception(generalExMsg);
+
+ var uniqueExceptions = exceptions.Distinct(new ExceptionEqualityComparer());
+
+ // If all the requests failed with the same exception (should be the case most of the time),
+ // just return one exception to represent them all.
+ if (uniqueExceptions.Count() == 1)
+ return uniqueExceptions.First();
+
+ // If all the requests failed but for different reasons, return an AggregateException
+ // with all the root-cause exceptions.
+ return new AggregateException(generalExMsg, uniqueExceptions);
+ }
+
+ ///
+ /// Used to aggregate exceptions that occur on request retries.
+ ///
+ ///
+ /// In most cases, the same exception will occur multiple times,
+ /// but we don't want to return multiple copies of it. This class is used
+ /// to find exceptions that are duplicates by type and message so we can
+ /// only return one of them.
+ ///
+ private class ExceptionEqualityComparer : IEqualityComparer
+ {
+ public bool Equals(Exception e1, Exception e2)
+ {
+ if (e2 == null && e1 == null)
+ return true;
+ else if (e1 == null | e2 == null)
+ return false;
+ else if (e1.GetType().Name.Equals(e2.GetType().Name) && e1.Message.Equals(e2.Message))
+ return true;
+ else
+ return false;
+ }
+
+ public int GetHashCode(Exception e)
+ {
+ return (e.GetType().Name + e.Message).GetHashCode();
+ }
+ }
+
}
+
}