C# Client Library
A C# Client Library for the AnalyzeRe REST API
Loading...
Searching...
No Matches
API.Requests.cs
Go to the documentation of this file.
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Diagnostics;
5using System.IO;
6using System.Linq;
7using System.Net;
8using System.Net.Sockets;
9
11using AnalyzeRe.Rest;
13using RestSharp;
14
15namespace AnalyzeRe
16{
18 public static partial class API
19 {
20#if DEBUG
23 public static volatile bool RequestLogging = true;
24#endif
25 #region OPTIONS
38 #endregion OPTIONS
39
40 #region Explicit Get Methods
52 public static T Get<T>(
53 string id,
55 int? timeout = null) where T : IAPIResource
56 {
57 ValidateResourceId(id);
59 return RequestAndParse<T>($"{collectionName}/{id}", Method.GET, requestParameters, timeout);
60 }
61
73 public static IAPIResource Get(Type resourceType,
74 string id,
76 int? timeout = null)
77 {
78 ValidateResourceId(id);
80 return (IAPIResource)RequestAndParse(resourceType, $"{collectionName}/{id}", Method.GET, requestParameters, timeout);
81 }
82
83 // TODO: This isn't well tested yet (what if we pass a path with a different base URL?)
98 public static T GetUri<T>(
99 Uri href,
101 int? timeout = null)
102 {
103 // TODO: What if the URL contains query parameters?
104 // How do we merge with GetOrPost requestParameters?
105 return RequestAndParse<T>(href.PathAndQuery, Method.GET, requestParameters, timeout);
106 }
107
122 public static object GetUri(Type deserializeType,
123 Uri href,
125 int? timeout = null)
126 {
127 return RequestAndParse(deserializeType, href.PathAndQuery, Method.GET,
129 }
130 #endregion Explicit Get Methods
131
132 #region GET Resource Collection
144 string collectionNameOverride = null,
145 int? timeout = null)
147 {
148 string collectionName = DetermineCollectionNameAndBaseType(typeof(T),
150
151 if (timeout == null)
153 if (typeof(T) == baseType)
156 // If we have requested a list of some derived type, we must
157 // determine the run-time list type to query.
158 return (ICollectionResponse<T>)GetDerivedResourceList(typeof(T),
160 }
161
172 Type resourceType,
174 string collectionNameOverride = null,
175 int? timeout = null)
176 {
177 string collectionName = DetermineCollectionNameAndBaseType(
179 return GetDerivedResourceList(resourceType, requestParameters,
181 }
182
183 #region Private Helper Methods
184 // Challenge - type constraints allow this method to be called with types derived from the
185 // base type, but when the results come back (for instance for
186 // `getResourceListGenericHelper{CatXL}()`), the deserializer needs to be able to
187 // parse the json as a List of Layers, not a List of CatXL, so we need to actually
188 // discover the base type that defines the collection endpoint at which the objects
189 // can be retrieved. Calling a generic method with a type parameter determined at run-time
190 // requires the ugly reflection at the end of this implementation. This wouldn't be a
191 // problem if the deserializer type parameter was passed in as a run-time type (like the
192 // Microsoft serialization class) rather than a compile time generic method type parameter.
193 // TODO: These reflection operations can benefit from caching.
194 private static ICollectionResponse<IAPIResource> GetDerivedResourceList(
195 Type resourceType,
197 int timeout,
198 string collectionName,
199 Type baseType)
200 {
201 if (resourceType != baseType)
202 {
203 // Try to determine a base resource type or interface that we can use to hold a
204 // list of server results.
205 if (baseType.ContainsGenericParameters) // TODO: Support something more elaborate
206 {
207 // This is a problem, we can't just instantiate something without generic
208 // arguments. We can attempt to fill them in from the constraints.
209 if (!baseType.IsInterface)
210 {
211 // This is a problem. We are about instantiate a sub-type with generic
212 // arguments. Even if these arguments are covariantly valid, this class
213 // can't be (because it's not an interface), so we won't be able to add
214 // elements to the list we're about to make, as covariance has to be
215 // supported all the way to the last generic argument to be able to store
216 // derived list elements. The solution is to guess at an interface
217 // implemented by this baseType that IS covariant. */
218
219 // Get any interfaces that have generic parameters
220 // TODO: can we do better than this guess?
222 .Where(i => i.ContainsGenericParameters);
223 // Pick the last applied interface with generic parameters.
224 baseType = baseInterfaces.Last().GetGenericTypeDefinition();
225 }
226 // Assuming there's only one generic argument, and assuming that its first
227 // generic parameter constraint is a covariantly valid interface parameter and
228 // matches the type the we got this interface from, this might work.
229 baseType = baseType.MakeGenericTypeFast(
230 baseType.GetGenericArguments()[0].GetGenericParameterConstraints()[0]);
231 // If we're lucky we now have an covariant base interface type for which
232 // elements of type T can be assigned.
233 }
234 }
235
236 // TODO: Server behaviour is not consistent / well enough defined yet to use its
237 // type searching in an automated way.
238 /*/ Create the query parameter that (once supported by the server) will filter by type.
239 Parameter typeSearchParameter = new Parameter
240 {
241 Type = ParameterType.GetOrPost,
242 Name = "type",
243 Value = TypeResolver.GetTypeNameForAPIType(resourceType)
244 };
245 requestParameters = (requestParameters ?? new Parameter[] { })
246 .Concat(new[] { typeSearchParameter });
247 */
248
249 // Execute the query on the server
252 // Parse the server response into a list of the base type
253 Type listOfBaseType = typeof(CollectionResponse<>).MakeGenericTypeFast(baseType);
256
257 // Filter result list to only items of type resourceType (which derive from baseType)
259 typeof(CollectionResponse<>).MakeGenericTypeFast(resourceType);
260 object targetTypeResult = Activator.CreateInstance(listOfResourceTypeType);
261 IList filteredResults = (IList)
262 Activator.CreateInstance(typeof(List<>).MakeGenericTypeFast(resourceType));
263
264 // HACK: To support sub-type-specific searches (which currently isn't supported server
265 // side, we filter results to the specified type.
266 // TODO: It doesn't make much sense to include meta if we are cutting down the results
267 // to a specific derived type.
268 listOfResourceTypeType.GetProperty(nameof(result.meta))
269 .SetValue(targetTypeResult, result.meta, null);
270 foreach (IAPIResource item in result.items.Where(resourceType.IsInstanceOfType))
271 {
273 }
274 listOfResourceTypeType.GetProperty(nameof(result.items))
276
278 }
279
280 // Helper method to resolve the resource collection name and the type that defined it.
281 private static string DetermineCollectionNameAndBaseType(
283 {
285 string collectionName;
286 try
287 {
290 }
291 catch (Exception e)
292 {
293 throw new ArgumentException(
294 "Could not resolve the collection name for this Resource, it must be " +
295 "provided manually via the 'collectionName' optional parameter.", e);
296 }
297 // If the defining type is generic, it is necessary to produce a
298 // covariant type which we can use to as a generic type
299 // parameter to an instantiable class wrapping this type.
300 if (baseType.IsGenericType)
302 return collectionName;
303 }
304 #endregion Private Helper Methods
305 #endregion GET Resource Collection
306
307 #region Search Resource Collection
309 public enum SearchType
310 {
312 Basic,
316 };
317
348
380 #endregion Search Resource Collection
381
382 #region Batch Requests
401 string collectionNameOverride = null,
402 int? timeout = null)
404 {
406 .AddParameters(Parameters.Page(0, ids.Count()))
407 .AddParameters(requestParameters),
409 }
410
427 Type resourceType,
430 string collectionNameOverride = null,
431 int? timeout = null)
432 {
434 .AddParameters(Parameters.Page(0, ids.Count()))
435 .AddParameters(requestParameters),
437 }
438 #endregion Batch Requests
439
440 #region Combined Request and Deserialization
465
491 #endregion Combined Request and Deserialization
492
493 #region Basic REST Request
514 string resource,
517 int? timeout = null,
518 bool returnRawResponse = false,
520 {
521 int attempts = 1;
524 do
525 {
526
528 // Add parameters (Cookies, HttpHeaders, GetOrPost Parameters, RequestBody...)
529 PrepareRequestParameters(ref request, requestParameters);
530 request.Timeout = timeout ?? DefaultRequestTimeout;
531
532 // If the desired timeout exceeds the default ReadWriteTimeout (5 minutes),
533 // increase it as well.
534 if (request.Timeout > 300000)
535 request.ReadWriteTimeout = request.Timeout;
536
537 // Set up the Stream reader if applicable
538 if (responseStreamReader != null)
539 request.ResponseWriter = responseStreamReader;
540
541 // Execute the request
542 IRestClient client = GetRestClient();
543 if(requestStartTime == null)
544 requestStartTime = DateTime.UtcNow;
545 response = ExecuteRequest(client, request);
546
547 // If configured not to check for HTTP status errors, return the response directly.
549 return response;
550
551 // If this gets set to true, the request may be retried
552 bool should_retry = false;
553
554 // Determine whether we've been issued a redirect request.
555 string redirection = CheckForRedirect(response, client);
556 // Change the resource to the new location's relative path
557 if (redirection != null)
558 {
560 should_retry = true;
561 }
562
563 // Returns TRUE if a temporary error was encountered and the request should be retried.
564 // Returns FALSE if the request completed successfully.
565 // Raises a meaningful exception if a permanent failure occurred.
567 CheckIncompleteResponse(response, requestStartTime.Value, attempts);
568
569 // If the request did not raise any communication errors, we can return the response.
570 if (!should_retry)
571 return response;
572
573 // In stream mode, we can't automatically retry the request (the stream has already been
574 // written to by the underlying RestSharp call and is tarnished) so we must raise an error.
575 if (responseStreamReader != null)
576 throw ServerErrorExceptionHelper(response, response.ErrorException, null,
577 "The initial request could not be completed due to a temporary " +
578 "communication error. Please reset the stream and try again. " +
579 "The error message was: " + response.ErrorMessage);
580
581 // If nothing above threw an exception, and should_retry is true, then the initial
582 // request failed and the issue might be resolved by attempting the same request again.
583
584 // This is not a standard feature of REST client libraries, but it
585 // reduces the overhead for client applications (like excel) by abstracting
586 // overarching configuration errors that would impact all individual requests.
587 } while (attempts++ <= MaxRequestRetries);
588
589 // If we exceeded the maximum number of retries and never returned, raise an error.
590 throw ServerErrorExceptionHelper(response, response.ErrorException, null,
591 "The request encountered what appears to be a temporary error, " +
592 "but continues to fail after " + MaxRequestRetries + " consecutive attempts. " +
593 "The final error message was: " + response.ErrorMessage);
594 }
595
596 private static IRestResponse ExecuteRequest(IRestClient client, IRestRequest request)
597 {
598#if DEBUG
599 if (RequestLogging)
600 DebugRestRequest(request);
601#endif
603#if DEBUG
604 // Only log the entire reset response if RequestLogging is turned on
605 // or if the response was an error.
606 if (RequestLogging || !response.IsSuccessful())
607 DebugRestResponse(response);
608#endif
609 return response;
610 }
611 #endregion Basic REST Request
612
613 #region IRestResponse deserialization
623 {
625 () => Converter.Deserialize<T>(response));
626 }
627
637 {
639 () => Converter.Deserialize(response.Content, deserializeType));
640 }
641 #endregion IRestResponse deserialization
642
643 #region Private Helper Methods
644 #region Request Helpers
651 private static void PrepareRequestParameters(
654 {
656
657 // If a global default authentication token has been configured, and no overriding
658 // Authorization header has been specified, add it to the request.
659 if (AuthenticationToken != null && !parameters.Any(p =>
660 p.Type == ParameterType.HttpHeader && p.Name.Equals(AUTH_HEADER)))
661 {
662 request.AddParameter(AUTH_HEADER, GetCurrentAuthorizationString(),
663 RestSharp.ParameterType.HttpHeader);
664 }
665
666 // Hack: Rather than have a separate optional argument for a request body to be
667 // serialized to JSON, we allow the Rest body parameter to be passed in as an
668 // object (usually it can only be a string), then we serialize it to JSON for them.
670 parameters.Where(param => param.Type == ParameterType.RequestBody).ToList();
671 if (requestBodyParameters.Count() > 1)
672 throw new ArgumentException("More than one RequestBody parameter was supplied.");
674
675 // Only serialize if we are sure it's meant to be JSON, but the body is not yet a string.
676 if (requestBodyParameter != null && requestBodyParameter.Name == JSON_CONTENT_TYPE &&
677 requestBodyParameter.Value.GetType() != typeof(string))
678 {
679 string serialized = Converter.Serialize(requestBodyParameter.Value);
680
681 // Remove the RequestBody parameter now that we've attached it to the request.
682 parameters.Remove(requestBodyParameter);
683 parameters.Add(requestBodyParameter = new Parameter
684 {
685 Name = JSON_CONTENT_TYPE,
686 ContentType = JSON_CONTENT_TYPE,
687 Value = serialized,
688 Type = ParameterType.RequestBody
689 });
690 }
691
692 // TODO: Experimental - zip the request body if it exceeds a certain size
693 //if (requestBodyParameter?.Value is string contentString && contentString.Length > 1)
694 //{
695 // // TODO: Either set the Content-Encoding
696 // request.AddParameter("Content-Encoding", "gzip", RestSharp.ParameterType.HttpHeader);
697 // // TODO: Or set the Content-Type (but probably not both)
698 // requestBodyParameter.Name = requestBodyParameter.ContentType = "application/gzip";
699
700 // MemoryStream dataStream = new MemoryStream();
701 // using (GZipStream zipStream = new GZipStream(dataStream, CompressionLevel.Fastest))
702 // using (StreamWriter writer = new StreamWriter(zipStream))
703 // {
704 // writer.Write(contentString);
705 // }
706 // requestBodyParameter.Value = dataStream.ToArray();
707 //}
708
709 request.Parameters.AddRange(parameters);
710 }
711 #endregion Request Helpers
712
713 #region Response Helpers
719 private static string CheckForRedirect(IRestResponse response, IRestClient client)
720 {
721 // Determine whether we've been issued a redirect request.
722 bool redirected = (int)response.StatusCode >= 300 && (int)response.StatusCode < 400;
723 if (!redirected)
724 return null;
725
726 string newLocation = response.Headers.FirstOrDefault(
727 h => h.Name.Equals("Location", StringComparison.OrdinalIgnoreCase))?.Value?.ToString();
728 string message = "The server returned a redirect status (" + response.StatusCode + ")";
729 Debug.WriteLine(message);
730 if (newLocation == null)
731 throw ServerErrorExceptionHelper(response, null, null, message +
732 ", but did specify a redirect location.");
733 // If the location is relative (presumably to the current host root), return it directly.
734 if (Uri.IsWellFormedUriString(newLocation, UriKind.Relative))
735 return newLocation;
736 // If the location is absolute, parse it
737 if (Uri.IsWellFormedUriString(newLocation, UriKind.Absolute))
738 {
739 Uri newUri = new Uri(newLocation, UriKind.Absolute);
740 // For now, assume it is unsafe to automatically redirect to a new host.
741 // The user will have to follow the redirect themselves manually if they trust it.
742 if (newUri.Host != client.BaseUrl.Host)
743 throw ServerErrorExceptionHelper(response, null, null, message +
744 ", but wants to switch to a new host URL (" + newUri.Host + "). " +
745 "You will have to manually reconfigure your client to use this " +
746 "new address if you trust it.");
747 // Return just the path (no host) to be used for the next request.
748 return newUri.AbsolutePath;
749 }
750 throw ServerErrorExceptionHelper(response, null, null, message +
751 ", but the redirect location was not well formed: " + newLocation);
752 }
753
764 private static bool CheckIncompleteResponse(IRestResponse response,
766 {
767 // Check if there were any authentication errors, and handle them appropriately
768 // Will return should_retry=true if the initial request failed but we can attempt
769 // this same request again (either because new credentials have been supplied,
770 // or if a temporary SSL failure may have occurred).
772 return true;
773
774 // If the request completed successfully, we're done here
775 if (response.ResponseStatus == ResponseStatus.Completed)
776 return false;
777
778 // If the request did not complete successfully, determine whether this is a temporary
779 // error (and return true to allow retry of the request) or raise a meaningful exception.
780
781 // The following exception types occur sporadically and should be automatically retried:
782 if (response.ErrorException is IOException || response.ErrorException is SocketException)
783 return true;
784
785 // Certain types of WebExceptions are also sporadic
786 if (response.ErrorException is WebException asWebException)
787 {
788 Debug.WriteLine("WebException Occurred with Status: " +
789 $"{asWebException.Status}\n{asWebException}");
790
791 // Timeouts are permanent failures (since the user configures the timeout), but
792 // some timeouts don't raise a meaningful exception.
793 if (asWebException.Status == WebExceptionStatus.Timeout)
794 return SuppressTimeoutOrThrow(response, requestStartTime);
795
796 // Sometimes a non-'timeout' error can occur because the timeout has elapsed.
797 // Detect these cases and treat them as a timeout.
798 // (See https://support.microsoft.com/en-us/help/2007873 )
799 // TODO: Such checks are culture (language) specific and may not work in other
800 // locales, Framework error messages might be translated.
801 const string socketTimeoutError = "A connection attempt failed because the " +
802 "connected party did not properly respond after a period of time, " +
803 "or established connection failed because connected host has failed to respond";
804 if ((asWebException.Status == WebExceptionStatus.KeepAliveFailure ||
805 asWebException.Status == WebExceptionStatus.ReceiveFailure) && (
806 asWebException.InnerException?.InnerException?.Message.Equals(
807 socketTimeoutError, StringComparison.CurrentCultureIgnoreCase) ?? false))
808 return SuppressTimeoutOrThrow(response, requestStartTime);
809
810 // If the status is indicative of a temporary error, suggest a retry
811 if (TemporaryFailureStatuses.Contains(asWebException.Status))
812 return true;
813 }
814 else if (response.ErrorMessage.Contains("The operation has timed out"))
815 return SuppressTimeoutOrThrow(response, requestStartTime);
816 // For all other errors, return the message from the server.
817 throw ServerErrorExceptionHelper(response, response.ErrorException, null,
818 $"Server Communication Error - {response.ErrorMessage}");
819 }
820
828 private static bool SuppressTimeoutOrThrow(IRestResponse response, DateTime requestStartTime)
829 {
830 TimeSpan elapsed = DateTime.UtcNow - requestStartTime;
831 string logPrefix = $"The request timed out after {elapsed.TotalMilliseconds} ms";
832
833 // Occasionally, a low-level timeout is raised before the response object is created.
834 // This is likely not an "actual" timeout and can be retried.
835 if (response?.Request == null)
836 {
837 Debug.WriteLine($"{logPrefix}, but the request object is empty. " +
838 "This likely indicates that a low-level timeout occurred before " +
839 "the response could be instantiated. The request will be retried.");
840 return true;
841 }
842
843 // If the timeout hasn't actually elapsed as far as we can tell, suggest a retry
844 if (response.Request.Timeout > elapsed.TotalMilliseconds)
845 {
846 Debug.WriteLine($"{logPrefix}, but the configured timeout period " +
847 $"({response.Request.Timeout} ms) hasn't elapsed. " +
848 "This likely indicates a timeout occurred in the underlying TCP protocol. " +
849 "The request will be retried.");
850 return true;
851 }
852
853 // Otherwise, re-raise as a timeout exception since that is more meaningful.
854 Debug.WriteLine($"{logPrefix}. Re-raising the error returned (" +
855 $"{response.ErrorException.GetType().NiceTypeName()}) as a RequestTimeoutException.");
856 string message = $"{logPrefix}. The requested timeout was {response.Request.Timeout} ms. " +
857 "If this is a large request, you may need to wait longer for a response. " +
858 "Check your API.DefaultRequestTimeout or set a timeout directly in your API call.";
859 throw new RequestTimeoutException(message, response.ErrorException, response);
860 }
861
872 private static T ExtractRestResponseHelper<T>(
874 Type deserializeType,
876 {
877 Exception parsingException = null;
878 if (response.IsSuccessful())
879 {
880 // See if the response can be parsed into the target object type.
881 try
882 {
883 T retVal;
884 // Response is empty, return null
885 if (response.StatusCode == HttpStatusCode.NoContent)
886 retVal = default(T);
887 // Special case: if user requests the response type of string,
888 // return the content directly without attempting to serialize it.
889 else if (deserializeType == typeof(string) || response.Content == null)
890 retVal = (T)Convert.ChangeType(response.Content, typeof(T));
891 // Similarly, if the user requests an IRestResponse result, return it directly.
892 else if (deserializeType.IsAssignableFrom(typeof(IRestResponse)))
893 retVal = (T)response;
894 // Otherwise, deserialize the content to the requested type
895 else
897#if DEBUG
898 if (RequestLogging)
899 {
900 Debug.WriteLine("The response was successfully parsed and returned.\n");
901 }
902#endif
903 return retVal;
904 }
905 catch (Exception e)
906 {
907 string parseMessage =
908 "The response could not be parsed into the desired type: \"" +
909 deserializeType.NiceTypeName() + "\"";
910#if DEBUG
911 Debug.WriteLine(parseMessage + (RequestLogging ? "\n" : ""));
912#endif
913 parsingException = new Exception(parseMessage, e);
914 }
915 }
916#if DEBUG
917 // If the request failed and request info level logging is turned off, then the request
918 // that failed won't have been logged yet, so log it now at the warning level.
919 if (!RequestLogging)
920 {
921 Debug.WriteLine("The following request failed:");
922 DebugRestRequest(response.Request);
923 }
924#endif
925
926 // See if the response can be parsed into an error object.
927 ServerError serverError = ExtractErrorResponse(response, ref parsingException);
928
929 // Throw an exception because the response was not successful.
930 // If we met a specific parsing exception, include it.
931 // If we parsed out a serverError object, include it.
932 throw ServerErrorExceptionHelper(response, parsingException, serverError);
933 }
934
941 private static ServerError ExtractErrorResponse(
943 ref Exception parsingException)
944 {
946 // Special case, we don't expect an error body for 503 responses
947 response.StatusCode != HttpStatusCode.ServiceUnavailable &&
948 // Otherwise, we expect non-successful responses to contain a ServerError
949 !response.IsSuccessful();
950
951 try
952 {
953 // Handle empty responses upfront rather letting Deserialize throw an exception.
954 if (String.IsNullOrWhiteSpace(response.Content))
955 {
956 // If the response is empty and we aren't expecting an error object, return
958 return null;
959 // Otherwise, complain that the response body was empty.
960 throw new ArgumentException(
961 "The server response was an error but no error details were supplied.");
962 }
963
964 ServerError serverError = Converter.Deserialize<ServerError>(response);
965 if (serverError.message == null && serverError.traceback == null)
966 throw new ArgumentException("Response contain no ServerError properties.");
967#if DEBUG
968 Debug.WriteLine("The response was received but contained a server error.");
969 if (RequestLogging) Debug.WriteLine("");
970#endif
971 return serverError;
972 }
973 catch (Exception e)
974 {
975 // If we weren't expecting an error response, ignore the failed attempt to get it.
976 // This happens in scenarios where we fail to parse the successful response, but
977 // before returning that parsing exception, we want to make sure the server didn't
978 // actually return an error response object in spite of the successful status code.
980 return null;
981 // If we were expecting an error response, and encountered an exception while
982 // trying to parse one out, return the parsing exception and log an error.
984#if DEBUG
985 Debug.WriteLine("The response was received but could not be parsed.");
986 if (RequestLogging) Debug.WriteLine("");
987#endif
988 return null;
989 }
990 }
991 #endregion Response Helpers
992 #endregion Private Helper Methods
993 }
994}
Used to deserialize server OPTIONS response.
Describes a collection of resources which can be listed.
Used to deserialize server error messages.
Definition ServerError.cs:9
Parameters that can be added to your REST requests to access additional API features.
static RequestParameters Ids(IEnumerable< string > ids)
Can be added to your collection GET requests to return only the set of resources whose id matches one...
static RequestParameters Search(string searchTerms)
Can be added to your GET requests to send a search request to the server. If the endpoint supports se...
static RequestParameters Page(long offset, long limit)
When getting a collection of items, these parameters can be added to restrict the number of results y...
static RequestParameters AdvancedSearch(string advancedSearchTerms)
Can be added to your GET requests to send a field filtering request to the server....
API methods / requests made available to the user.
static object GetUri(Type deserializeType, Uri href, IEnumerable< Parameter > requestParameters=null, int? timeout=null)
Perform a GET on an arbitrary resource by extracting the path and query parameters from a manually sp...
static OptionsResponse Options(IEnumerable< Parameter > requestParameters=null, int? timeout=null)
Perform an OPTIONS request on the API, which returns a list of features supported for the current aut...
static IAccessToken AuthenticationToken
The AccessToken storing authentication information for requests made to the server.
static int DefaultRequestTimeout
The default timeout used for all simple server requests, in milliseconds.
static volatile int MaxRequestRetries
When a temporary communication failure occurs (such as a socket error or an authentication failure fo...
static T RequestAndParse< T >(string resource, Method method, IEnumerable< Parameter > requestParameters=null, int? timeout=null)
Perform a REST request on the server and serializes the response to the desired compile-time type.
static T GetUri< T >(Uri href, IEnumerable< Parameter > requestParameters=null, int? timeout=null)
Perform a GET on an arbitrary resource by extracting the path and query parameters from a manually sp...
static string GetCollectionName< T >()
Gets the collection name for the given APIResource type, whether it's instantiable or not.
static ICollectionResponse< IAPIResource > GetResourceList(Type resourceType, IEnumerable< Parameter > requestParameters=null, string collectionNameOverride=null, int? timeout=null)
Get a collection of resources from the server.
static ICollectionResponse< T > BatchGet< T >(IEnumerable< string > ids, IEnumerable< Parameter > requestParameters=null, string collectionNameOverride=null, int? timeout=null)
Get a collection of resources from the server that match the set of resource ids specified.
static bool TryHandleAuthenticationErrors(IRestResponse response, int sequentialAttempts=0)
Checks an IRestResponse object for authentication errors, then invokes configured authentication dele...
static string GetCurrentAuthorizationString()
Return the Authorization header string formed by the current AuthenticationToken.
static T ExtractRestResponse< T >(IRestResponse response)
Attempts to deserialize an IRestResponse content to the specified type T.
static int DefaultRequestTimeoutCollections
The default timeout used when requesting a resource collection from the server, in milliseconds.
static object ExtractRestResponse(IRestResponse response, Type deserializeType)
Attempts to deserialize an IRestResponse content to the specified run-time type.
static ICollectionResponse< IAPIResource > SearchResourceList(Type resourceType, string searchTerms, IEnumerable< Parameter > requestParameters=null, string collectionNameOverride=null, int? timeout=null, SearchType searchType=SearchType.Basic)
Get a collection of resources from the server that match the specified search criteria.
static object RequestAndParse(Type deserializeType, string resource, Method method, IEnumerable< Parameter > requestParameters=null, int? timeout=null)
Perform a REST request on the server and serializes the response to the desired run-time type.
static ICollectionResponse< T > SearchResourceList< T >(string searchTerms, IEnumerable< Parameter > requestParameters=null, string collectionNameOverride=null, int? timeout=null, SearchType searchType=SearchType.Basic)
Get a collection of resources from the server that match the specified search criteria.
static IAPIResource Get(Type resourceType, string id, IEnumerable< Parameter > requestParameters=null, int? timeout=null)
Get the selected resource by ID. The appropriate collection is automatically determined by the suppli...
SearchType
The search type determines the interpretation of the query string.
@ Basic
Search by keyword on predefined field.
@ Advanced
Search by complex query on key-value metadata. See Parameters.AdvancedSearch for details about advanc...
static string GetCollectionName(Type requestType)
Gets the collection name for the given APIResource type, whether it's instantiable or not.
static ICollectionResponse< IAPIResource > BatchGet(Type resourceType, IEnumerable< string > ids, IEnumerable< Parameter > requestParameters=null, string collectionNameOverride=null, int? timeout=null)
Get a collection of resources from the server that match the set of resource ids specified.
static IRestResponse ExecuteRestRequest(string resource, Method method, IEnumerable< Parameter > requestParameters=null, int? timeout=null, bool returnRawResponse=false, Action< Stream > responseStreamReader=null)
Perform a REST request on the server.
static T Get< T >(string id, IEnumerable< Parameter > requestParameters=null, int? timeout=null)
Get the selected resource by ID. The appropriate collection is automatically determined by the resour...
static ICollectionResponse< T > GetResourceList< T >(IEnumerable< Parameter > requestParameters=null, string collectionNameOverride=null, int? timeout=null)
Get a collection of resources from the server.
Extends the TimeoutException class to contain the IRestResponse that timed out.
Helper class which makes it easier to build a set of request parameters.
Utility for resolving types given only the types name (Useful for parsing ambiguous JSON objects).
static Type GetGenericCovariantBase(Type T)
Returns the most strongly typed covariant base type which is assignable from the specified type T.
Interface for Base class used by all resources.