Exemple #1
0
        /// <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));
                }
        }
Exemple #2
0
        /// <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);
        }
Exemple #4
0
        /// <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);
        }
Exemple #6
0
        /// <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);
        }
Exemple #8
0
        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);
                }
            }
        }
Exemple #9
0
        /// <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);
        }
Exemple #11
0
        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);
                }
            }
        }
Exemple #12
0
        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);
                }
        }
Exemple #16
0
        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);
        }