2using System.Collections.Concurrent;
3using System.Collections.Generic;
4using System.Diagnostics;
5using System.Globalization;
8using System.Reflection;
10using System.Threading;
11using System.Threading.Tasks;
22 public static partial class API
24 internal const string DebugTimeFormat =
@"yyyy-MM-dd HH:mm:ss.FFFF";
29 #region Public Utilities
48 response.Headers.Any(
h =>
h.Name ==
"ARE-Server")))
86 if (
pollingOptions.CancellationToken?.IsCancellationRequested ??
false)
103 $"The request is still not complete after {elapsed_ms / 1000d} seconds, which " +
104 $"exceeds the timeout of {pollingOptions.MaxPollTotalTime / 1000d}s.",
108 Debug.WriteLine(
$"Polling completed after {count} attempts (took {elapsed_ms / 1000d} seconds).");
155 throw new Exception(
"The server replied successfully, but the response was null.");
175 $"but it could not be parsed: {retryAfter.Value}",
176 ex,
ex.RestResponse,
ex.ServerError);
188 $"but it could not be parsed: {queueHeader.Value}",
189 ex,
ex.RestResponse,
ex.ServerError);
203 string suffix = ((ex.MaxWaitTime ?? 0
d) <= 0
d ?
"" :
204 $"The maximum wait time of {ex.MaxWaitTime} seconds has been exceeded. ") +
205 "Please try again later.";
206 string message =
null;
209 message +=
$"Simulation is queued at position {queuePosition}. ";
211 message +=
$"Server suggests retrying after {suggestedWaitSeconds} seconds. ";
220 requestEx.RestResponse :
ex.RestResponse);
225 #region Get Collection Name
226 #region Method Overloads
260 #endregion Method Overloads
306 "does not derive from IAPIResource.");
313 BindingFlags.FlattenHierarchy | BindingFlags.Public |
BindingFlags.Static;
315 BindingFlags.FlattenHierarchy | BindingFlags.Public |
BindingFlags.Instance;
351 throw new Exception(
"The type " +
requestType.NiceTypeName() +
352 " does not define or inherit a collection_name property.");
401 ". Type can be linked to more than one resource (" +
collectionName +
411 throw new Exception(
"Error determining the bass class for the collection " +
439 int guid_start = href.LastIndexOf(
'/', href.Length - 2);
443 #endregion Get Collection Name
444 #endregion Public Utilities
446 #region Internal Utilities / Helper Methods
447 internal const string ERR_MISSING_ID =
448 "The specified resource's 'id' property is missing. " +
449 "Did you forget to POST this resource and store the result? For example:\n" +
450 "var saved_resource = staged_resource.Post();";
461 return $"{resource.collection_name}/{resource.id}";
473 return $"{resource.collection_name}/";
503 internal static void ValidateResourceId(
string id)
505 if (String.IsNullOrEmpty(
id))
509 #endregion Internal Utilities / Helper Methods
511 #region Private Utilities / Helper Methods
522 message = (
response !=
null ?
"Server threw " + response.StatusCode
523 :
"Server error") +
": " +
serverErr.message.TrimEnd(
'.');
529 message =
"Server response content error: " +
inner.Message.TrimEnd(
'.') +
530 ". Response Content: " + TryFormat(
response.Content) +
531 "\n See inner exception and restResponse properties for details.";
535 message =
"Server response error. Response Status: " +
536 response.StatusCode +
", Content: " + TryFormat(
response.Content) +
537 ". See restResponse property for details.";
540 else if (
inner !=
null)
542 message =
"Exception encountered: " +
inner.Message.TrimEnd(
'.') +
543 ". See inner exception for details.";
548 #region Debug Helper Methods
563 #region Debug Printing Extension Methods
574 result.AppendLine(
"Processing the following request: ");
575 result.AppendLine(
$"{pad}Current Time: {DateTime.UtcNow.ToString(DebugTimeFormat)}");
576 result.AppendLine(
$"{pad}Request Method: {request.Method}");
577 result.AppendLine(
$"{pad}Request Resource: " +
578 (String.IsNullOrWhiteSpace(
request.Resource) ?
"(root)" :
request.Resource));
580 result.Append(String.Join(
"",
request.Parameters.OrderByDescending(p => p.Type)
581 .ThenBy(p => p.Name).Where(p => p.Type !=
ParameterType.RequestBody)
582 .GroupBy(p => p.Type).Select(
g =>
$"{pad}{g.Key} Params:".
PadRight(20 +
indent) +
583 String.Join(
"\n" +
new string(
' ', 20 +
indent),
584 g.Select(
px =>
$"{px.Name} = \"{px.Value}\"")) +
"\n")));
589 result.AppendLine(
pad +
"Request Body (" +
body.Name +
"): " +
590 TryFormat(
body.Value,
body.Name == JSON_CONTENT_TYPE,
indent,
true));
593 result.Append(
pad +
"Request Body: (null)");
594 return result.ToString();
608 result.AppendLine(
"The following response was received:");
610 result.AppendLine(
"Error while receiving a response from the server:");
611 result.AppendLine(
$"{pad}Current Time: {DateTime.UtcNow.ToString(DebugTimeFormat)}");
614 result.AppendLine(
$"{pad}Response Status: {response.ResponseStatus}");
615 result.Append(
$"{pad}Response Error: {response.ErrorMessage}");
617 return result.ToString();
626 result.Append(
pad +
"Received '503 - Service Unavailable' with " +
627 (
retryHeader ==
null ?
"no Retry-After header" :
$"Retry-After: {retryHeader.Value}") +
628 (
queuePosition ==
null ?
" and no Queue-Position header" :
$" and Queue-Position: {queuePosition.Value}"));
629 return result.ToString();
632 result.AppendLine(
$"{pad}Response URI: {response.ResponseUri}");
633 result.AppendLine(
$"{pad}Response HTTP Code: " +
634 $"({(int)response.StatusCode}) {response.StatusDescription}");
635 result.AppendLine(
$"{pad}Response Headers: " +
636 String.Join(
"\n" +
new string(
' ', 20 +
indent),
637 response.Headers.Select(p =>
$"{p.Name}: \"{p.Value}\"").OrderBy(
s =>
s)));
639 null :
$" ({response.ContentType})";
640 result.Append(
pad +
$"Response Body{contentType}: ".
PadRight(20) +
642 return result.ToString();
651 return response !=
null && response.ResponseStatus == ResponseStatus.Completed &&
654 #endregion Debug Printing Extension Methods
664 private static string TryFormat(
object content,
bool tryJSON =
false,
int indent = 0,
667 if (content ==
null)
return "(null)";
668 string retVal = content.ToString();
669 if (String.IsNullOrWhiteSpace(
retVal))
670 return "(response content was either empty or got redirected to a user-defined stream)";
671 if (
retVal.Length >= 10000)
672 return $"{retVal.Substring(0, 1000)}... " +
673 $"(response content is too long to display in full: {retVal.Length} characters)";
697 #endregion Debug Helper Methods
698 #endregion Private Utilities / Helper Methods
700 #region Deprecated Methods
723 [
Obsolete(
"This method has been replaced with an overload that uses a PollingOptions object")]
733 #endregion Deprecated Methods
A custom exception class that includes the RestSharp.IRestResponse that generated the exception,...
Describes a collection of resources which can be listed.
Used to deserialize server error messages.
API methods / requests made available to the user.
static string PrettyPrint(this IRestRequest request)
Extension method that generates a helpful string representation of an IRestRequest.
static int DefaultRequestTimeout
The default timeout used for all simple server requests, in milliseconds.
static bool IsAReServerActive(string checkServerURL, int? timeout=null)
Determines if the provided URL is a valid running Analyze Re server.
static string GetCollectionName< T >()
Gets the collection name for the given APIResource type, whether it's instantiable or not.
static bool IsSuccessful(this IRestResponse response)
Extension method that returns true if the restResponse was completed and contains a successful status...
static void PollUntil(Func< bool > request, AnalyzeRe.PollingOptions pollingOptions=null)
Poll the specified request until it returns true.
static string PrettyPrint(this IRestResponse response)
Extension method that generates a helpful string representation of an IRestResponse.
static string GetCollectionName(Type requestType, out Type declaringClassType, bool suppress_throw=false)
Gets the collection name for the given APIResource type, whether it's instantiable or not.
static T PollUntilReady< T >(Func< T > functionThatMight503, AnalyzeRe.PollingOptions pollingOptions=null)
Specialized polling that will executes the specified request delegate, but if a 503 retry-after APIRe...
static readonly string AnalyzeReServerIdentity
The Server identity returned by valid AnalyzeRe servers.
static string GetCollectionName(Type requestType)
Gets the collection name for the given APIResource type, whether it's instantiable or not.
static TResponse PollUntil< TResponse >(Func< TResponse > request, Func< TResponse, bool > isPollingComplete, AnalyzeRe.PollingOptions pollingOptions=null)
Poll the requested resource until a desired state is attained.
static string GetCollectionNameFromResourceHref(string href)
Return the collection name implied by the href for a given resource assuming the URL is in the format...
Thrown when a request requires additional time to complete, but it exceeds the time we are willing to...
Determines the behaviour of the API when automatically retrying a request whose result is not yet rea...
static PollingOptions Default
The default PollingArguments used when none are specified.
Helper class which makes it easier to build a set of request parameters.
RequestParameters AddParameter(Parameter parameter)
Adds the specified parameter to this list and returns the list.
Utility for resolving types given only the types name (Useful for parsing ambiguous JSON objects).
static List< Type > GetAllInstantiableSubtypes(Type type)
Attempts to create a list of types in the current application domain's assemblies derived from the gi...
Interface for Base class used by all resources.