/// <summary> /// Authorizes an application installation, generating an access token for the given shop. /// </summary> /// <param name="code">Authorization code used to retrieve an access_token for the user. Can only be used once.</param> /// <param name="webFlowAppKey">Unique ID for your application.</param> /// <param name="webFlowAppSecretKey"> Private value unique to your application.</param> /// <returns>The authorization result.</returns> public static async Task <WebFlowAuthorizationResult> AuthorizeWithResult(string code, string webFlowAppKey, string webFlowAppSecretKey) { var ub = new UriBuilder(BuildWebFlowUri("api.webflow.com")) { Path = "oauth/access_token" }; var content = new JsonContent(new { code, client_secret = webFlowAppSecretKey, client_id = webFlowAppKey, grant_type = "authorization_code" }); using (var client = new HttpClient()) using (var msg = new CloneableRequestMessage(ub.Uri, HttpMethod.Post, content)) { var request = client.SendAsync(msg); var response = await request; var rawDataString = await response.Content.ReadAsStringAsync(); CheckResponseExceptions(response, rawDataString); return(JsonConvert.DeserializeObject <WebFlowAuthorizationResult>(rawDataString)); } }
/// <summary> /// Authorizes an application installation, generating an access token for the given shop. /// </summary> /// <param name="clientId">App id</param> /// <param name="clientSecret">App secret key</param> /// <param name="refreshToken">refresh token.</param> /// <returns>The shop access token.</returns> public static async Task <WixAuthorizationResult> GenerateNewAccessToken(string clientId, string clientSecret, string refreshToken) { var ub = new UriBuilder(WixService.BuildWixUri("www.wix.com")) { Path = "oauth/access" }; var content = new JsonContent(new { refresh_token = refreshToken, client_secret = clientSecret, client_id = clientId, grant_type = "refresh_token" }); using (var client = new HttpClient()) using (var msg = new CloneableRequestMessage(ub.Uri, HttpMethod.Post, content)) { var request = client.SendAsync(msg); var response = await request; var rawDataString = await response.Content.ReadAsStringAsync(); WixService.CheckResponseExceptions(response, rawDataString); return(JsonConvert.DeserializeObject <WixAuthorizationResult>(rawDataString)); } }
/// <summary> /// Prepares a request to the path and appends the shop's access token header if applicable. /// </summary> protected CloneableRequestMessage PrepareRequestMessage(RequestUri uri, HttpMethod method, HttpContent content = null) { var msg = new CloneableRequestMessage(uri.ToUri(), method, content); msg.Headers.Add("Accept", "application/json"); return(msg); }
/// <summary> /// Authorizes an application installation, generating an access token for the given shop. /// </summary> /// <param name="code">The authorization code generated by Shopify, which should be a parameter named 'code' on the request querystring.</param> /// <param name="myShopifyUrl">The store's *.myshopify.com URL, which should be a paramter named 'shop' on the request querystring.</param> /// <param name="shopifyApiKey">Your app's public API key.</param> /// <param name="shopifySecretKey">Your app's secret key.</param> /// <returns>The shop access token.</returns> public static async Task <string> Authorize(string code, string myShopifyUrl, string shopifyApiKey, string shopifySecretKey) { var ub = new UriBuilder(ShopifyService.BuildShopUri(myShopifyUrl, false)) { Path = "admin/oauth/access_token" }; var content = new JsonContent(new { client_id = shopifyApiKey, client_secret = shopifySecretKey, code, }); using (var client = new HttpClient()) using (var msg = new CloneableRequestMessage(ub.Uri, HttpMethod.Post, content)) { var request = client.SendAsync(msg); var response = await request; var rawDataString = await response.Content.ReadAsStringAsync(); ShopifyService.CheckResponseExceptions(response, rawDataString); var json = JToken.Parse(rawDataString); return(json.Value <string>("access_token")); } }
/// <summary> /// Prepares a request to the path and appends the shop's access token header if applicable. /// </summary> protected CloneableRequestMessage PrepareRequestMessage(RequestUri uri, HttpMethod method, HttpContent content = null) { var msg = new CloneableRequestMessage(uri.ToUri(), method, content); msg.Headers.Add("Accept", "application/json"); msg.Headers.Add("user-agent", "QuickbutikSharp"); msg.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{_apiKey}:{_apiKey}"))); return(msg); }
/// <summary> /// Prepares a request to the path and appends the shop's access token header if applicable. /// </summary> protected virtual CloneableRequestMessage PrepareRequestMessage(RequestUri uri, HttpMethod method, HttpContent content = null) { var msg = new CloneableRequestMessage(uri.ToUri(), method, content); if (!string.IsNullOrEmpty(_AccessToken)) { msg.Headers.Add("Authorization", $@"Bearer {_AccessToken}"); } msg.Headers.Add("Accept", "application/json"); return(msg); }
/// <summary> /// Prepares a request to the path and appends the shop's access token header if applicable. /// </summary> protected CloneableRequestMessage PrepareRequestMessage(RequestUri uri, HttpMethod method, HttpContent content = null) { var msg = new CloneableRequestMessage(uri.ToUri(), method, content); if (!string.IsNullOrEmpty(_AccessToken)) { msg.Headers.Add("X-Shopify-Access-Token", _AccessToken); } return(msg); }
public async Task <RequestResult <T> > Run <T>(CloneableRequestMessage baseRequest, ExecuteRequestAsync <T> executeRequestAsync) { var accessToken = GetAccessToken(baseRequest); LeakyBucket bucket = null; if (accessToken != null) { bucket = _shopAccessTokenToLeakyBucket.GetOrAdd(accessToken, _ => new LeakyBucket()); } while (true) { var request = baseRequest.Clone(); if (accessToken != null) { await bucket.GrantAsync(); } try { var fullResult = await executeRequestAsync(request); var bucketState = LeakyBucketState.Get(fullResult.Response); if (bucketState != null) { bucket?.SetState(bucketState); } return(fullResult); } catch (ShopifyRateLimitException ex) when(ex.Reason == ShopifyRateLimitReason.BucketFull || !_retryOnlyIfLeakyBucketFull) { //Only retry if breach caused by full bucket //Other limits will bubble the exception because it's not clear how long the program should wait //Even if there is a Retry-After header, we probably don't want the thread to sleep for potentially many hours // //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> /// Prepares a request to the path and appends the shop's access token header if applicable. /// </summary> protected CloneableRequestMessage PrepareRequestMessage(RequestUri uri, HttpMethod method, HttpContent content = null) { var msg = new CloneableRequestMessage(uri.ToUri(), method, content); if (!string.IsNullOrEmpty(_secretApiKey)) { msg.Headers.Add("Authorization", $"Bearer {_secretApiKey}"); } msg.Headers.Add("Accept", "application/json"); msg.Headers.Add("user-agent", "SquareSpaceSharp"); if (method == HttpMethod.Post) { msg.Headers.Add("Idempotency-Key", _secretApiKey); } return(msg); }
/// <summary> /// Prepares a request to the path and appends the shop's access token header if applicable. /// </summary> protected CloneableRequestMessage PrepareRequestMessage(RequestUri uri, HttpMethod method, HttpContent content = null) { var msg = new CloneableRequestMessage(uri.ToUri(), method, content); if (!string.IsNullOrEmpty(_accessToken)) { msg.Headers.Add("Authorization", $"Bearer {_accessToken}"); } //msg.Headers.Add("Accept", "application/json"); msg.Headers.Add("accept-version", "1.0.0"); if (method == HttpMethod.Post) { msg.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString()); } return(msg); }
public async Task <T> Run <T>(CloneableRequestMessage baseRequest, ExecuteRequestAsync <T> executeRequestAsync) { while (true) { var request = baseRequest.Clone(); try { var fullResult = await executeRequestAsync(request); return(fullResult.Result); } catch (BizzebeeRateLimitException) { await Task.Delay(RETRY_DELAY); } } }
public async Task <T> Run <T>(CloneableRequestMessage baseRequest, ExecuteRequestAsync <T> executeRequestAsync) { var accessToken = GetAccessToken(baseRequest); LeakyBucket bucket = null; if (accessToken != null) { bucket = _shopAccessTokenToLeakyBucket.GetOrAdd(accessToken, _ => new LeakyBucket()); } while (true) { var request = baseRequest.Clone(); if (accessToken != null) { await bucket.GrantAsync(); } try { var fullResult = await executeRequestAsync(request); var bucketState = GetBucketState(fullResult.Response); if (bucketState != null) { bucket?.SetState(bucketState); } return(fullResult.Result); } catch (SquareSpaceRateLimitException) { //An exception may still occur: //-SquareSpace may have a slightly different algorithm //-SquareSpace 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); } } }
public async Task <RequestResult <T> > Run <T>(CloneableRequestMessage baseRequest, ExecuteRequestAsync <T> executeRequestAsync) { while (true) { var request = baseRequest.Clone(); try { var fullResult = await executeRequestAsync(request); return(fullResult); } catch (ShopifyRateLimitException ex) when(ex.Reason == ShopifyRateLimitReason.BucketFull || !_retryOnlyIfLeakyBucketFull) { //Only retry if breach caused by full bucket //Other limits will bubble the exception because it's not clear how long the program should wait //Even if there is a Retry-After header, we probably don't want the thread to sleep for potentially many hours await Task.Delay(RETRY_DELAY); } } }
/// <summary> /// Prepares a request to the path and appends the shop's access token header if applicable. /// </summary> protected CloneableRequestMessage PrepareRequestMessage(RequestUri uri, HttpMethod method, HttpContent content, Dictionary <string, string> headers) { var msg = new CloneableRequestMessage(uri.ToUri(), method, content); if (!string.IsNullOrEmpty(_AccessToken)) { msg.Headers.Add("X-Shopify-Access-Token", _AccessToken); } msg.Headers.Add("Accept", "application/json"); if (headers != null) { foreach (var kv in headers) { msg.Headers.Add(kv.Key, kv.Value); } } return(msg); }
/// <summary> /// Authorizes an application installation, generating an access token for the given shop. /// </summary> /// <param name="authCode"></param> /// <param name="myTictailUrl"> /// The store's *.tictail.com URL, which should be a paramter named 'shop' on the request /// querystring. /// </param> /// <param name="tictailApiKey">Your app's public API key.</param> /// <param name="tictailSecretKey">Your app's secret key.</param> /// <param name="scopes"></param> /// <returns>The authorization result.</returns> public static async Task <Root> AuthorizeWithResult(string authCode, string myTictailUrl, string tictailApiKey, string tictailSecretKey, IEnumerable <string> scopes) { var ub = new UriBuilder(TictailService.BuildShopUri(myTictailUrl, false)) { Path = "oauth/token" }; var values = new Dictionary <string, string> { { "client_id", $"{tictailApiKey}" }, { "client_secret", $"{tictailSecretKey}" }, { "code", $"{authCode}" }, { "grant_type", "authorization_code" }, { "scope", $"[{string.Join(",", scopes)}]" }, }; var content = new FormUrlEncodedContent(values); using (var client = new HttpClient()) using (var msg = new CloneableRequestMessage(ub.Uri, HttpMethod.Post, content)) { var request = client.SendAsync(msg); var response = await request; var rawDataString = await response.Content.ReadAsStringAsync(); TictailService.CheckResponseExceptions(response, rawDataString); //var json = JToken.Parse(rawDataString); //var a = new AuthorizationResult(json.Value<string>("access_token"), // json.Value<string>("scope").Split(','), json.Value<TictailStore>("store")); var json = JsonConvert.DeserializeObject <Root>(rawDataString); //return new AuthorizationResult(json.Value<string>("access_token"), // json.Value<string>("scope").Split(','), json.Value<TictailStore>("store")); return(json); } }
public async Task <RequestResult <T> > Run <T>(CloneableRequestMessage baseRequest, ExecuteRequestAsync <T> executeRequestAsync, CancellationToken cancellationToken, int?graphqlQueryCost = null) { var accessToken = GetAccessToken(baseRequest); var bucket = accessToken == null ? null : _shopAccessTokenToLeakyBucket.GetOrAdd(accessToken, _ => new MultiShopifyAPIBucket(_getRequestContext)); bool isGraphQL = baseRequest.RequestUri.AbsolutePath.EndsWith("graphql.json"); while (true) { var request = baseRequest.Clone(); if (isGraphQL) { //if the user didn't pass a request query cost, we assume a cost of 50 graphqlQueryCost = graphqlQueryCost ?? 50; if (bucket != null) { await bucket.WaitForAvailableGraphQLAsync(graphqlQueryCost.Value, cancellationToken); } var res = await executeRequestAsync(request); var json = res.Result as JToken; if (bucket != null) { var cost = json.SelectToken("extensions.cost"); if (cost != null) { var throttleStatus = cost["throttleStatus"]; int maximumAvailable = (int)throttleStatus["maximumAvailable"]; int restoreRate = (int)throttleStatus["restoreRate"]; int currentlyAvailable = (int)throttleStatus["currentlyAvailable"]; int actualQueryCost = (int?)cost["actualQueryCost"] ?? graphqlQueryCost.Value; //actual query cost is null if THROTTLED int refund = graphqlQueryCost.Value - actualQueryCost; //may be negative if user didn't supply query cost bucket.SetGraphQLBucketState(maximumAvailable, restoreRate, currentlyAvailable, refund); //The user might have supplied no cost or an invalid cost //We fix the query cost so the correct value is used if a retry is needed graphqlQueryCost = (int)cost["requestedQueryCost"]; } } if (json.SelectToken("errors") ?.Children() .Any(r => r.SelectToken("extensions.code")?.Value <string>() == "THROTTLED") == true) { await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); continue; } return(res); } else //REST { try { if (bucket != null) { await bucket.WaitForAvailableRESTAsync(cancellationToken); } var res = await executeRequestAsync(request); if (bucket != null) { var apiCallLimitHeaderValue = GetRestCallLimit(res.Response); if (apiCallLimitHeaderValue != null) { var split = apiCallLimitHeaderValue.Split('/'); if (split.Length == 2 && int.TryParse(split[0], out int currentlyUsed) && int.TryParse(split[1], out int maxAvailable)) { bucket.SetRESTBucketState(maxAvailable, maxAvailable - currentlyUsed); } } } return(res); } catch (ShopifyRateLimitException ex) when(ex.Reason == ShopifyRateLimitReason.BucketFull || !_retryRESTOnlyIfLeakyBucketFull) { //Only retry if breach caused by full bucket //Shopify sometimes return 429 for other limits (e.g if too many variants are created) await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); } } } }
public async Task <RequestResult <T> > Run <T>(CloneableRequestMessage request, ExecuteRequestAsync <T> executeRequestAsync, CancellationToken cancellationToken) { var fullResult = await executeRequestAsync(request); return(fullResult); }
public async Task <T> Run <T>(CloneableRequestMessage request, ExecuteRequestAsync <T> executeRequestAsync) { var fullResult = await executeRequestAsync(request); return(fullResult.Result); }