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
12using AnalyzeRe.Rest;
14using RestSharp;
15
16namespace AnalyzeRe
17{
19 public static partial class API
20 {
21#if DEBUG
24 public static volatile bool RequestLogging = true;
25#endif
26 #region OPTIONS
39 #endregion OPTIONS
40
41 #region Explicit Get Methods
53 public static T Get<T>(
54 string id,
56 int? timeout = null) where T : IAPIResource
57 {
58 ValidateResourceId(id);
60 return RequestAndParse<T>($"{collectionName}/{id}", Method.GET, requestParameters, timeout);
61 }
62
74 public static IAPIResource Get(Type resourceType,
75 string id,
77 int? timeout = null)
78 {
79 ValidateResourceId(id);
81 return (IAPIResource)RequestAndParse(resourceType, $"{collectionName}/{id}", Method.GET, requestParameters, timeout);
82 }
83
84 // TODO: This isn't well tested yet (what if we pass a path with a different base URL?)
99 public static T GetUri<T>(
100 Uri href,
102 int? timeout = null)
103 {
104 // TODO: What if the URL contains query parameters?
105 // How do we merge with GetOrPost requestParameters?
106 return RequestAndParse<T>(href.PathAndQuery, Method.GET, requestParameters, timeout);
107 }
108
123 public static object GetUri(Type deserializeType,
124 Uri href,
126 int? timeout = null)
127 {
128 return RequestAndParse(deserializeType, href.PathAndQuery, Method.GET,
130 }
131 #endregion Explicit Get Methods
132
133 #region GET Resource Collection
145 string collectionNameOverride = null,
146 int? timeout = null)
148 {
149 string collectionName = DetermineCollectionNameAndBaseType(typeof(T),
151
152 if (timeout == null)
154 if (typeof(T) == baseType)
157 // If we have requested a list of some derived type, we must
158 // determine the run-time list type to query.
159 return (ICollectionResponse<T>)GetDerivedResourceList(typeof(T),
161 }
162
173 Type resourceType,
175 string collectionNameOverride = null,
176 int? timeout = null)
177 {
178 string collectionName = DetermineCollectionNameAndBaseType(
180 return GetDerivedResourceList(resourceType, requestParameters,
182 }
183
184 #region Private Helper Methods
185 // Challenge - type constraints allow this method to be called with types derived from the
186 // base type, but when the results come back (for instance for
187 // `getResourceListGenericHelper{CatXL}()`), the deserializer needs to be able to
188 // parse the json as a List of Layers, not a List of CatXL, so we need to actually
189 // discover the base type that defines the collection endpoint at which the objects
190 // can be retrieved. Calling a generic method with a type parameter determined at run-time
191 // requires the ugly reflection at the end of this implementation. This wouldn't be a
192 // problem if the deserializer type parameter was passed in as a run-time type (like the
193 // Microsoft serialization class) rather than a compile time generic method type parameter.
194 // TODO: These reflection operations can benefit from caching.
195 private static ICollectionResponse<IAPIResource> GetDerivedResourceList(
196 Type resourceType,
198 int timeout,
199 string collectionName,
200 Type baseType)
201 {
202 if (resourceType != baseType)
203 {
204 // Try to determine a base resource type or interface that we can use to hold a
205 // list of server results.
206 if (baseType.ContainsGenericParameters) // TODO: Support something more elaborate
207 {
208 // This is a problem, we can't just instantiate something without generic
209 // arguments. We can attempt to fill them in from the constraints.
210 if (!baseType.IsInterface)
211 {
212 // This is a problem. We are about instantiate a sub-type with generic
213 // arguments. Even if these arguments are covariantly valid, this class
214 // can't be (because it's not an interface), so we won't be able to add
215 // elements to the list we're about to make, as covariance has to be
216 // supported all the way to the last generic argument to be able to store
217 // derived list elements. The solution is to guess at an interface
218 // implemented by this baseType that IS covariant. */
219
220 // Get any interfaces that have generic parameters
221 // TODO: can we do better than this guess?
223 .Where(i => i.ContainsGenericParameters);
224 // Pick the last applied interface with generic parameters.
225 baseType = baseInterfaces.Last().GetGenericTypeDefinition();
226 }
227 // Assuming there's only one generic argument, and assuming that its first
228 // generic parameter constraint is a covariantly valid interface parameter and
229 // matches the type the we got this interface from, this might work.
230 baseType = baseType.MakeGenericTypeFast(
231 baseType.GetGenericArguments()[0].GetGenericParameterConstraints()[0]);
232 // If we're lucky we now have an covariant base interface type for which
233 // elements of type T can be assigned.
234 }
235 }
236
237 // TODO: Server behaviour is not consistent / well enough defined yet to use its
238 // type searching in an automated way.
239 /*/ Create the query parameter that (once supported by the server) will filter by type.
240 Parameter typeSearchParameter = new Parameter
241 {
242 Type = ParameterType.GetOrPost,
243 Name = "type",
244 Value = TypeResolver.GetTypeNameForAPIType(resourceType)
245 };
246 requestParameters = (requestParameters ?? new Parameter[] { })
247 .Concat(new[] { typeSearchParameter });
248 */
249
250 // Execute the query on the server
253 // Parse the server response into a list of the base type
254 Type listOfBaseType = typeof(CollectionResponse<>).MakeGenericTypeFast(baseType);
257
258 // Filter result list to only items of type resourceType (which derive from baseType)
260 typeof(CollectionResponse<>).MakeGenericTypeFast(resourceType);
261 object targetTypeResult = Activator.CreateInstance(listOfResourceTypeType);
262 IList filteredResults = (IList)
263 Activator.CreateInstance(typeof(List<>).MakeGenericTypeFast(resourceType));
264
265 // HACK: To support sub-type-specific searches (which currently isn't supported server
266 // side, we filter results to the specified type.
267 // TODO: It doesn't make much sense to include meta if we are cutting down the results
268 // to a specific derived type.
269 listOfResourceTypeType.GetProperty(nameof(result.meta))
270 .SetValue(targetTypeResult, result.meta, null);
271 foreach (IAPIResource item in result.items.Where(resourceType.IsInstanceOfType))
272 {
274 }
275 listOfResourceTypeType.GetProperty(nameof(result.items))
277
279 }
280
281 // Helper method to resolve the resource collection name and the type that defined it.
282 private static string DetermineCollectionNameAndBaseType(
284 {
286 string collectionName;
287 try
288 {
291 }
292 catch (Exception e)
293 {
294 throw new ArgumentException(
295 "Could not resolve the collection name for this Resource, it must be " +
296 "provided manually via the 'collectionName' optional parameter.", e);
297 }
298 // If the defining type is generic, it is necessary to produce a
299 // covariant type which we can use to as a generic type
300 // parameter to an instantiable class wrapping this type.
301 if (baseType.IsGenericType)
303 return collectionName;
304 }
305 #endregion Private Helper Methods
306 #endregion GET Resource Collection
307
308 #region Search Resource Collection
310 public enum SearchType
311 {
313 Basic,
317 };
318
349
381 #endregion Search Resource Collection
382
383 #region Batch Requests
402 string collectionNameOverride = null,
403 int? timeout = null)
405 {
407 .AddParameters(Parameters.Page(0, ids.Count()))
408 .AddParameters(requestParameters),
410 }
411
428 Type resourceType,
431 string collectionNameOverride = null,
432 int? timeout = null)
433 {
435 .AddParameters(Parameters.Page(0, ids.Count()))
436 .AddParameters(requestParameters),
438 }
439 #endregion Batch Requests
440
441 #region Combined Request and Deserialization
466
492 #endregion Combined Request and Deserialization
493
494 #region Basic REST Request
515 string resource,
518 int? timeout = null,
519 bool returnRawResponse = false,
521 {
522 int attempts = 1;
525 do
526 {
527
529 // Add parameters (Cookies, HttpHeaders, GetOrPost Parameters, RequestBody...)
530 PrepareRequestParameters(ref request, requestParameters);
531 request.Timeout = timeout ?? DefaultRequestTimeout;
532
533 // If the desired timeout exceeds the default ReadWriteTimeout (5 minutes),
534 // increase it as well.
535 if (request.Timeout > 300000)
536 request.ReadWriteTimeout = request.Timeout;
537
538 // Set up the Stream reader if applicable
539 if (responseStreamReader != null)
540 request.ResponseWriter = responseStreamReader;
541
542 // Execute the request
543 IRestClient client = GetRestClient();
544 if(requestStartTime == null)
545 requestStartTime = DateTime.UtcNow;
546 response = ExecuteRequest(client, request);
547
548 // If configured not to check for HTTP status errors, return the response directly.
550 return response;
551
552 // If this gets set to true, the request may be retried
553 bool should_retry = false;
554
555 // Determine whether we've been issued a redirect request.
556 string redirection = CheckForRedirect(response, client);
557 // Change the resource to the new location's relative path
558 if (redirection != null)
559 {
561 should_retry = true;
562 }
563
564 // Returns TRUE if a temporary error was encountered and the request should be retried.
565 // Returns FALSE if the request completed successfully.
566 // Raises a meaningful exception if a permanent failure occurred.
568 CheckIncompleteResponse(response, requestStartTime.Value, attempts);
569
570 // If the request did not raise any communication errors, we can return the response.
571 if (!should_retry)
572 return response;
573
574 // In stream mode, we can't automatically retry the request (the stream has already been
575 // written to by the underlying RestSharp call and is tarnished) so we must raise an error.
576 if (responseStreamReader != null)
577 throw ServerErrorExceptionHelper(response, response.ErrorException, null,
578 "The initial request could not be completed due to a temporary " +
579 "communication error. Please reset the stream and try again. " +
580 "The error message was: " + response.ErrorMessage);
581
582 // If nothing above threw an exception, and should_retry is true, then the initial
583 // request failed and the issue might be resolved by attempting the same request again.
584
585 // This is not a standard feature of REST client libraries, but it
586 // reduces the overhead for client applications (like excel) by abstracting
587 // overarching configuration errors that would impact all individual requests.
588 } while (attempts++ <= MaxRequestRetries);
589
590 // If we exceeded the maximum number of retries and never returned, raise an error.
591 throw ServerErrorExceptionHelper(response, response.ErrorException, null,
592 "The request encountered what appears to be a temporary error, " +
593 "but continues to fail after " + MaxRequestRetries + " consecutive attempts. " +
594 "The final error message was: " + response.ErrorMessage);
595 }
596
597 private static IRestResponse ExecuteRequest(IRestClient client, IRestRequest request)
598 {
599#if DEBUG
600 if (RequestLogging)
601 DebugRestRequest(request);
602#endif
604#if DEBUG
605 // Only log the entire reset response if RequestLogging is turned on
606 // or if the response was an error.
607 if (RequestLogging || !response.IsSuccessful())
608 DebugRestResponse(response);
609#endif
610 return response;
611 }
612 #endregion Basic REST Request
613
614 #region IRestResponse deserialization
624 {
626 () => Converter.Deserialize<T>(response));
627 }
628
638 {
640 () => Converter.Deserialize(response.Content, deserializeType));
641 }
642 #endregion IRestResponse deserialization
643
644 #region Private Helper Methods
645 #region Request Helpers
652 private static void PrepareRequestParameters(
655 {
657
658 // Check if an overriding Authorization header has been specified
659 if (!parameters.Any(p => p.Type == ParameterType.HttpHeader && p.Name.Equals("Authorization")))
661 {
662 request.AddHeader("Authorization", $"Bearer {bearerToken.access_token}");
663 }
665 {
666 request.AddHeader("Authorization", $"Basic {basicToken.access_token}");
667 }
668
669 // Hack: Rather than have a separate optional argument for a request body to be
670 // serialized to JSON, we allow the Rest body parameter to be passed in as an
671 // object (usually it can only be a string), then we serialize it to JSON for them.
673 parameters.Where(param => param.Type == ParameterType.RequestBody).ToList();
674 if (requestBodyParameters.Count() > 1)
675 throw new ArgumentException("More than one RequestBody parameter was supplied.");
677
678 // Only serialize if we are sure it's meant to be JSON, but the body is not yet a string.
679 if (requestBodyParameter != null && requestBodyParameter.Name == JSON_CONTENT_TYPE &&
680 requestBodyParameter.Value.GetType() != typeof(string))
681 {
682 string serialized = Converter.Serialize(requestBodyParameter.Value);
683
684 // Remove the RequestBody parameter now that we've attached it to the request.
685 parameters.Remove(requestBodyParameter);
686 parameters.Add(requestBodyParameter = new Parameter
687 {
688 Name = JSON_CONTENT_TYPE,
689 ContentType = JSON_CONTENT_TYPE,
690 Value = serialized,
691 Type = ParameterType.RequestBody
692 });
693 }
694
695 // TODO: Experimental - zip the request body if it exceeds a certain size
696 //if (requestBodyParameter?.Value is string contentString && contentString.Length > 1)
697 //{
698 // // TODO: Either set the Content-Encoding
699 // request.AddParameter("Content-Encoding", "gzip", RestSharp.ParameterType.HttpHeader);
700 // // TODO: Or set the Content-Type (but probably not both)
701 // requestBodyParameter.Name = requestBodyParameter.ContentType = "application/gzip";
702
703 // MemoryStream dataStream = new MemoryStream();
704 // using (GZipStream zipStream = new GZipStream(dataStream, CompressionLevel.Fastest))
705 // using (StreamWriter writer = new StreamWriter(zipStream))
706 // {
707 // writer.Write(contentString);
708 // }
709 // requestBodyParameter.Value = dataStream.ToArray();
710 //}
711
712 request.Parameters.AddRange(parameters);
713 }
714 #endregion Request Helpers
715
716 #region Response Helpers
722 private static string CheckForRedirect(IRestResponse response, IRestClient client)
723 {
724 // Determine whether we've been issued a redirect request.
725 bool redirected = (int)response.StatusCode >= 300 && (int)response.StatusCode < 400;
726 if (!redirected)
727 return null;
728
729 string newLocation = response.Headers.FirstOrDefault(
730 h => h.Name.Equals("Location", StringComparison.OrdinalIgnoreCase))?.Value?.ToString();
731 string message = "The server returned a redirect status (" + response.StatusCode + ")";
732 Debug.WriteLine(message);
733 if (newLocation == null)
734 throw ServerErrorExceptionHelper(response, null, null, message +
735 ", but did specify a redirect location.");
736 // If the location is relative (presumably to the current host root), return it directly.
737 if (Uri.IsWellFormedUriString(newLocation, UriKind.Relative))
738 return newLocation;
739 // If the location is absolute, parse it
740 if (Uri.IsWellFormedUriString(newLocation, UriKind.Absolute))
741 {
742 Uri newUri = new Uri(newLocation, UriKind.Absolute);
743 // For now, assume it is unsafe to automatically redirect to a new host.
744 // The user will have to follow the redirect themselves manually if they trust it.
745 if (newUri.Host != client.BaseUrl.Host)
746 throw ServerErrorExceptionHelper(response, null, null, message +
747 ", but wants to switch to a new host URL (" + newUri.Host + "). " +
748 "You will have to manually reconfigure your client to use this " +
749 "new address if you trust it.");
750 // Return just the path (no host) to be used for the next request.
751 return newUri.AbsolutePath;
752 }
753 throw ServerErrorExceptionHelper(response, null, null, message +
754 ", but the redirect location was not well formed: " + newLocation);
755 }
756
767 private static bool CheckIncompleteResponse(IRestResponse response,
769 {
770 // Check if there were any authentication errors, and handle them appropriately
771 // Will return should_retry=true if the initial request failed but we can attempt
772 // this same request again (either because new credentials have been supplied,
773 // or if a temporary SSL failure may have occurred).
775 return true;
776
777 // If the request completed successfully, we're done here
778 if (response.ResponseStatus == ResponseStatus.Completed)
779 return false;
780
781 // If the request did not complete successfully, determine whether this is a temporary
782 // error (and return true to allow retry of the request) or raise a meaningful exception.
783
784 // The following exception types occur sporadically and should be automatically retried:
785 if (response.ErrorException is IOException || response.ErrorException is SocketException)
786 return true;
787
788 // Certain types of WebExceptions are also sporadic
789 if (response.ErrorException is WebException asWebException)
790 {
791 Debug.WriteLine("WebException Occurred with Status: " +
792 $"{asWebException.Status}\n{asWebException}");
793
794 // Timeouts are permanent failures (since the user configures the timeout), but
795 // some timeouts don't raise a meaningful exception.
796 if (asWebException.Status == WebExceptionStatus.Timeout)
797 return SuppressTimeoutOrThrow(response, requestStartTime);
798
799 // Sometimes a non-'timeout' error can occur because the timeout has elapsed.
800 // Detect these cases and treat them as a timeout.
801 // (See https://support.microsoft.com/en-us/help/2007873 )
802 // TODO: Such checks are culture (language) specific and may not work in other
803 // locales, Framework error messages might be translated.
804 const string socketTimeoutError = "A connection attempt failed because the " +
805 "connected party did not properly respond after a period of time, " +
806 "or established connection failed because connected host has failed to respond";
807 if ((asWebException.Status == WebExceptionStatus.KeepAliveFailure ||
808 asWebException.Status == WebExceptionStatus.ReceiveFailure) && (
809 asWebException.InnerException?.InnerException?.Message.Equals(
810 socketTimeoutError, StringComparison.CurrentCultureIgnoreCase) ?? false))
811 return SuppressTimeoutOrThrow(response, requestStartTime);
812
813 // If the status is indicative of a temporary error, suggest a retry
814 if (TemporaryFailureStatuses.Contains(asWebException.Status))
815 return true;
816 }
817 else if (response.ErrorMessage.Contains("The operation has timed out"))
818 return SuppressTimeoutOrThrow(response, requestStartTime);
819 // For all other errors, return the message from the server.
820 throw ServerErrorExceptionHelper(response, response.ErrorException, null,
821 $"Server Communication Error - {response.ErrorMessage}");
822 }
823
831 private static bool SuppressTimeoutOrThrow(IRestResponse response, DateTime requestStartTime)
832 {
833 TimeSpan elapsed = DateTime.UtcNow - requestStartTime;
834 string logPrefix = $"The request timed out after {elapsed.TotalMilliseconds} ms";
835
836 // Occasionally, a low-level timeout is raised before the response object is created.
837 // This is likely not an "actual" timeout and can be retried.
838 if (response?.Request == null)
839 {
840 Debug.WriteLine($"{logPrefix}, but the request object is empty. " +
841 "This likely indicates that a low-level timeout occurred before " +
842 "the response could be instantiated. The request will be retried.");
843 return true;
844 }
845
846 // If the timeout hasn't actually elapsed as far as we can tell, suggest a retry
847 if (response.Request.Timeout > elapsed.TotalMilliseconds)
848 {
849 Debug.WriteLine($"{logPrefix}, but the configured timeout period " +
850 $"({response.Request.Timeout} ms) hasn't elapsed. " +
851 "This likely indicates a timeout occurred in the underlying TCP protocol. " +
852 "The request will be retried.");
853 return true;
854 }
855
856 // Otherwise, re-raise as a timeout exception since that is more meaningful.
857 Debug.WriteLine($"{logPrefix}. Re-raising the error returned (" +
858 $"{response.ErrorException.GetType().NiceTypeName()}) as a RequestTimeoutException.");
859 string message = $"{logPrefix}. The requested timeout was {response.Request.Timeout} ms. " +
860 "If this is a large request, you may need to wait longer for a response. " +
861 "Check your API.DefaultRequestTimeout or set a timeout directly in your API call.";
862 throw new RequestTimeoutException(message, response.ErrorException, response);
863 }
864
875 private static T ExtractRestResponseHelper<T>(
877 Type deserializeType,
879 {
880 Exception parsingException = null;
881 if (response.IsSuccessful())
882 {
883 // See if the response can be parsed into the target object type.
884 try
885 {
886 T retVal;
887 // Response is empty, return null
888 if (response.StatusCode == HttpStatusCode.NoContent)
889 retVal = default(T);
890 // Special case: if user requests the response type of string,
891 // return the content directly without attempting to serialize it.
892 else if (deserializeType == typeof(string) || response.Content == null)
893 retVal = (T)Convert.ChangeType(response.Content, typeof(T));
894 // Similarly, if the user requests an IRestResponse result, return it directly.
895 else if (deserializeType.IsAssignableFrom(typeof(IRestResponse)))
896 retVal = (T)response;
897 // Otherwise, deserialize the content to the requested type
898 else
900#if DEBUG
901 if (RequestLogging)
902 {
903 Debug.WriteLine("The response was successfully parsed and returned.\n");
904 }
905#endif
906 return retVal;
907 }
908 catch (Exception e)
909 {
910 string parseMessage =
911 "The response could not be parsed into the desired type: \"" +
912 deserializeType.NiceTypeName() + "\"";
913#if DEBUG
914 Debug.WriteLine(parseMessage + (RequestLogging ? "\n" : ""));
915#endif
916 parsingException = new Exception(parseMessage, e);
917 }
918 }
919#if DEBUG
920 // If the request failed and request info level logging is turned off, then the request
921 // that failed won't have been logged yet, so log it now at the warning level.
922 if (!RequestLogging)
923 {
924 Debug.WriteLine("The following request failed:");
925 DebugRestRequest(response.Request);
926 }
927#endif
928
929 // See if the response can be parsed into an error object.
930 ServerError serverError = ExtractErrorResponse(response, ref parsingException);
931
932 // Throw an exception because the response was not successful.
933 // If we met a specific parsing exception, include it.
934 // If we parsed out a serverError object, include it.
935 throw ServerErrorExceptionHelper(response, parsingException, serverError);
936 }
937
944 private static ServerError ExtractErrorResponse(
946 ref Exception parsingException)
947 {
949 // Special case, we don't expect an error body for 503 responses
950 response.StatusCode != HttpStatusCode.ServiceUnavailable &&
951 // Otherwise, we expect non-successful responses to contain a ServerError
952 !response.IsSuccessful();
953
954 try
955 {
956 // Handle empty responses upfront rather letting Deserialize throw an exception.
957 if (String.IsNullOrWhiteSpace(response.Content))
958 {
959 // If the response is empty and we aren't expecting an error object, return
961 return null;
962 // Otherwise, complain that the response body was empty.
963 throw new ArgumentException(
964 "The server response was an error but no error details were supplied.");
965 }
966
967 ServerError serverError = Converter.Deserialize<ServerError>(response);
968 if (serverError.message == null && serverError.traceback == null)
969 throw new ArgumentException("Response contain no ServerError properties.");
970#if DEBUG
971 Debug.WriteLine("The response was received but contained a server error.");
972 if (RequestLogging) Debug.WriteLine("");
973#endif
974 return serverError;
975 }
976 catch (Exception e)
977 {
978 // If we weren't expecting an error response, ignore the failed attempt to get it.
979 // This happens in scenarios where we fail to parse the successful response, but
980 // before returning that parsing exception, we want to make sure the server didn't
981 // actually return an error response object in spite of the successful status code.
983 return null;
984 // If we were expecting an error response, and encountered an exception while
985 // trying to parse one out, return the parsing exception and log an error.
987#if DEBUG
988 Debug.WriteLine("The response was received but could not be parsed.");
989 if (RequestLogging) Debug.WriteLine("");
990#endif
991 return null;
992 }
993 }
994 #endregion Response Helpers
995 #endregion Private Helper Methods
996 }
997}
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 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.
An AccessToken storing basic authentication information for requests made to the server.
An AccessToken storing Bearer authentication information for requests made to 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.