public async Task <T> Run <T>(BizwebRequestMessage baseReqMsg, ExecuteRequestAsync <T> executeRequestAsync) { var tryCount = 0; Start: try { using (var reqMsg = baseReqMsg.Clone()) { tryCount++; return((await executeRequestAsync(reqMsg)).Result); } } catch (ApiRateLimitException) { if (tryCount >= MAX_RETRY) { throw; } await Task.Delay(RETRY_DELAY); goto Start; } }
/// <summary> /// Executes a request and returns the raw result string. Throws an exception when the response is invalid. /// </summary> public static async Task <string> ExecuteRequestToStringAsync(BizwebRequestMessage requestMsg, IRequestExecutionPolicy execPolicy) { if (requestMsg.Content != null) { //necessary to buffer content for multiple reads await requestMsg.Content.LoadIntoBufferAsync(); } return(await execPolicy.Run(requestMsg, async (reqMsg) => { //Need to create a RequestInfo before send RequestMessage //because after that, HttpClient will dispose RequestMessage var requestInfo = await CreateRequestSimpleInfoAsync(reqMsg); using (var response = await HttpUtils.SendHttpRequestAsync(reqMsg)) { //Check for and throw exception when necessary. await CheckResponseExceptionsAsync(response, requestInfo); var rawResponse = await response.Content.ReadAsStringAsync(); return new RequestResult <string>(response, rawResponse); } })); }
/// <summary> /// Executes a request and returns the JToken result. Throws an exception when the response is invalid. /// </summary> public static async Task <JToken> ExecuteRequestAsync(BizwebRequestMessage requestMsg, IRequestExecutionPolicy execPolicy) { if (requestMsg.Content != null) { //necessary to buffer content for multiple reads await requestMsg.Content.LoadIntoBufferAsync(); } return(await execPolicy.Run(requestMsg, async (reqMsg) => { //Need to create a RequestInfo before send RequestMessage //because after that, HttpClient will dispose RequestMessage var requestInfo = await CreateRequestSimpleInfoAsync(reqMsg); using (var response = await HttpUtils.SendHttpRequestAsync(reqMsg)) { //Check for and throw exception when necessary. await CheckResponseExceptionsAsync(response, requestInfo); // When using JToken make sure that dates are not stripped of any timezone information // if tokens are de-serialised into strings/DateTime/DateTimeZoneOffset using (var reader = new JsonTextReader(new StreamReader(await response.Content.ReadAsStreamAsync())) { DateParseHandling = DateParseHandling.None }) { //Notice: deserialize can fails when response body null or empty var result = Deserialize(reader, reqMsg.RootElement); return new RequestResult <JToken>(response, result); } } })); }
public async Task <T> Run <T>(BizwebRequestMessage baseReqMsg, ExecuteRequestAsync <T> executeRequestAsync) { var accessToken = GetAccessToken(baseReqMsg); LeakyBucket bucket = null; if (accessToken != null) { bucket = LeakyBucket.GetBucketByToken(accessToken); } while (true) { using (var reqMsg = baseReqMsg.Clone()) { if (accessToken != null) { await bucket.GrantAsync(); } try { var fullResult = await executeRequestAsync(reqMsg); var bucketState = GetBucketState(fullResult.Response); var reportedFillLevel = bucketState.Item1; var reportedCapacity = bucketState.Item2; if (reportedFillLevel != null && reportedCapacity != null) { bucket?.SetBucketState(reportedFillLevel.Value, reportedCapacity.Value); } return(fullResult.Result); } catch (BizwebSharpException) { //An exception may still occur: //-Shopify may have a slightly different algorithm //-Shopify may change to a different algorithm in the future //-There may be timing and latency delays //-Multiple programs may use the same access token //-Multiple instances of the same program may use the same access token await Task.Delay(THROTTLE_DELAY); } } } }
/// <summary> /// Creates an <see cref="BizwebRequestMessage"/> by setting the method and the necessary authenticate information. /// </summary> /// <param name="authState">The Bizweb authenticate state.</param> /// <param name="pathAndQuery">The request's path.</param> /// <param name="method">The <see cref="HttpMethod"/> to use for the request.</param> /// <param name="content">The <see cref="HttpContent"/> to use for the request.</param> /// <param name="rootElement">The root element to deserialize. Default is null.</param> /// <returns>The prepared <see cref="BizwebRequestMessage"/>.</returns> public static BizwebRequestMessage CreateRequest(BizwebAuthorizationState authState, string pathAndQuery, HttpMethod method, HttpContent content = null, string rootElement = null) { var baseUri = BuildUri(authState.ApiUrl); var endPointUri = new Uri(baseUri, pathAndQuery); var msg = new BizwebRequestMessage(endPointUri, method, content, rootElement); if (!string.IsNullOrEmpty(authState.AccessToken)) { msg.Headers.Add(ApiConst.HEADER_KEY_ACCESS_TOKEN, authState.AccessToken); msg.Headers.Add("Cache-Control", "no-cache"); } msg.Headers.Add("Accept", "application/json"); return(msg); }
public BizwebRequestMessage Clone() { var newContent = Content; if (newContent != null && newContent is JsonContent c) { newContent = c.Clone(); } var cloned = new BizwebRequestMessage(RequestUri, Method, newContent, RootElement); // Copy over the request's headers which includes the access token if set foreach (var header in Headers) { cloned.Headers.Add(header.Key, header.Value); } return(cloned); }
public async Task <T> Run <T>(BizwebRequestMessage baseReqMsg, ExecuteRequestAsync <T> executeRequestAsync) { while (true) { try { using (var reqMsg = baseReqMsg.Clone()) { var fullResult = await executeRequestAsync(reqMsg); return(fullResult.Result); } } catch (ApiRateLimitException) { await Task.Delay(RETRY_DELAY); } } }
/// <summary> /// Executes a request and returns the given type. Throws an exception when the response is invalid. /// Use this method when the expected response is a single line or simple object that doesn't warrant its own class. /// </summary> public static async Task <T> ExecuteRequestAsync <T>(BizwebRequestMessage requestMsg) where T : new() { return(await ExecuteRequestAsync <T>(requestMsg, DefaultRequestExecutionPolicy.Default)); }
/// <summary> /// Executes a request and returns the given type. Throws an exception when the response is invalid. /// Use this method when the expected response is a single line or simple object that doesn't warrant its own class. /// </summary> public static async Task <T> ExecuteRequestAsync <T>(BizwebRequestMessage requestMsg, IRequestExecutionPolicy execPolicy) where T : new() { return((await ExecuteRequestAsync(requestMsg, execPolicy)).ToObject <T>()); }
/// <summary> /// Executes a request and returns the JToken result. Throws an exception when the response is invalid. /// </summary> public static async Task <JToken> ExecuteRequestAsync(BizwebRequestMessage requestMsg) { return(await ExecuteRequestAsync(requestMsg, DefaultRequestExecutionPolicy.Default)); }
/// <summary> /// Executes a request and returns the raw result string. Throws an exception when the response is invalid. /// </summary> public static async Task <string> ExecuteRequestToStringAsync(BizwebRequestMessage requestMsg) { return(await ExecuteRequestToStringAsync(requestMsg, DefaultRequestExecutionPolicy.Default)); }
public async Task <T> Run <T>(BizwebRequestMessage requestMsg, ExecuteRequestAsync <T> executeRequestAsync) { return((await executeRequestAsync(requestMsg)).Result); }