ruby_shopify_api 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/ISSUE_TEMPLATE.md +36 -0
- data/.github/probots.yml +2 -0
- data/.github/workflows/build.yml +43 -0
- data/.gitignore +15 -0
- data/.rubocop.yml +28 -0
- data/.rubocop_todo.yml +75 -0
- data/CHANGELOG-OLD.md +501 -0
- data/CHANGELOG.md +5 -0
- data/CONTRIBUTING.md +8 -0
- data/CONTRIBUTORS +3 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +151 -0
- data/Gemfile_ar41 +5 -0
- data/Gemfile_ar50 +5 -0
- data/Gemfile_ar51 +5 -0
- data/Gemfile_ar60 +5 -0
- data/Gemfile_ar_main +5 -0
- data/LICENSE +20 -0
- data/README.md +649 -0
- data/RELEASING +17 -0
- data/Rakefile +55 -0
- data/SECURITY.md +59 -0
- data/dev.yml +11 -0
- data/docker-compose.yml +13 -0
- data/docs/_config.yml +1 -0
- data/docs/_includes/footer.html +28 -0
- data/docs/_includes/head.html +28 -0
- data/docs/_layouts/index.html +57 -0
- data/docs/graphql.md +241 -0
- data/docs/index.md +639 -0
- data/lib/active_resource/connection_ext.rb +11 -0
- data/lib/active_resource/detailed_log_subscriber.rb +55 -0
- data/lib/active_resource/json_errors.rb +37 -0
- data/lib/shopify_api/api_access.rb +57 -0
- data/lib/shopify_api/api_version.rb +206 -0
- data/lib/shopify_api/connection.rb +36 -0
- data/lib/shopify_api/countable.rb +15 -0
- data/lib/shopify_api/disable_prefix_check.rb +31 -0
- data/lib/shopify_api/events.rb +8 -0
- data/lib/shopify_api/graphql/http_client.rb +22 -0
- data/lib/shopify_api/graphql/railtie.rb +17 -0
- data/lib/shopify_api/graphql/task.rake +100 -0
- data/lib/shopify_api/graphql.rb +103 -0
- data/lib/shopify_api/hmac_params.rb +33 -0
- data/lib/shopify_api/limits.rb +77 -0
- data/lib/shopify_api/message_enricher.rb +25 -0
- data/lib/shopify_api/meta.rb +14 -0
- data/lib/shopify_api/metafields.rb +21 -0
- data/lib/shopify_api/paginated_collection.rb +69 -0
- data/lib/shopify_api/pagination_link_headers.rb +34 -0
- data/lib/shopify_api/resources/abandoned_checkout.rb +7 -0
- data/lib/shopify_api/resources/access_scope.rb +10 -0
- data/lib/shopify_api/resources/access_token.rb +9 -0
- data/lib/shopify_api/resources/address.rb +5 -0
- data/lib/shopify_api/resources/announcement.rb +5 -0
- data/lib/shopify_api/resources/api_permission.rb +9 -0
- data/lib/shopify_api/resources/application_charge.rb +16 -0
- data/lib/shopify_api/resources/application_credit.rb +5 -0
- data/lib/shopify_api/resources/array_base.rb +13 -0
- data/lib/shopify_api/resources/article.rb +22 -0
- data/lib/shopify_api/resources/asset.rb +101 -0
- data/lib/shopify_api/resources/assigned_fulfillment_order.rb +16 -0
- data/lib/shopify_api/resources/base.rb +166 -0
- data/lib/shopify_api/resources/billing_address.rb +5 -0
- data/lib/shopify_api/resources/blog.rb +11 -0
- data/lib/shopify_api/resources/carrier_service.rb +5 -0
- data/lib/shopify_api/resources/cart.rb +5 -0
- data/lib/shopify_api/resources/checkout.rb +30 -0
- data/lib/shopify_api/resources/collect.rb +7 -0
- data/lib/shopify_api/resources/collection.rb +14 -0
- data/lib/shopify_api/resources/collection_listing.rb +19 -0
- data/lib/shopify_api/resources/collection_publication.rb +10 -0
- data/lib/shopify_api/resources/comment.rb +24 -0
- data/lib/shopify_api/resources/country.rb +5 -0
- data/lib/shopify_api/resources/currency.rb +6 -0
- data/lib/shopify_api/resources/custom_collection.rb +20 -0
- data/lib/shopify_api/resources/customer.rb +30 -0
- data/lib/shopify_api/resources/customer_group.rb +6 -0
- data/lib/shopify_api/resources/customer_invite.rb +5 -0
- data/lib/shopify_api/resources/customer_saved_search.rb +12 -0
- data/lib/shopify_api/resources/discount_code.rb +10 -0
- data/lib/shopify_api/resources/discount_code_batch.rb +34 -0
- data/lib/shopify_api/resources/draft_order.rb +15 -0
- data/lib/shopify_api/resources/draft_order_invoice.rb +5 -0
- data/lib/shopify_api/resources/event.rb +9 -0
- data/lib/shopify_api/resources/fulfillment.rb +56 -0
- data/lib/shopify_api/resources/fulfillment_event.rb +16 -0
- data/lib/shopify_api/resources/fulfillment_order.rb +151 -0
- data/lib/shopify_api/resources/fulfillment_order_locations_for_move.rb +5 -0
- data/lib/shopify_api/resources/fulfillment_request.rb +16 -0
- data/lib/shopify_api/resources/fulfillment_service.rb +5 -0
- data/lib/shopify_api/resources/fulfillment_v2.rb +21 -0
- data/lib/shopify_api/resources/gift_card.rb +8 -0
- data/lib/shopify_api/resources/image.rb +17 -0
- data/lib/shopify_api/resources/inventory_item.rb +6 -0
- data/lib/shopify_api/resources/inventory_level.rb +54 -0
- data/lib/shopify_api/resources/line_item.rb +15 -0
- data/lib/shopify_api/resources/location.rb +8 -0
- data/lib/shopify_api/resources/marketing_event.rb +11 -0
- data/lib/shopify_api/resources/metafield.rb +14 -0
- data/lib/shopify_api/resources/note_attribute.rb +5 -0
- data/lib/shopify_api/resources/option.rb +5 -0
- data/lib/shopify_api/resources/order.rb +44 -0
- data/lib/shopify_api/resources/order_risk.rb +9 -0
- data/lib/shopify_api/resources/page.rb +7 -0
- data/lib/shopify_api/resources/payment.rb +7 -0
- data/lib/shopify_api/resources/payment_details.rb +5 -0
- data/lib/shopify_api/resources/ping.rb +3 -0
- data/lib/shopify_api/resources/policy.rb +8 -0
- data/lib/shopify_api/resources/price_rule.rb +8 -0
- data/lib/shopify_api/resources/product.rb +59 -0
- data/lib/shopify_api/resources/product_listing.rb +17 -0
- data/lib/shopify_api/resources/product_publication.rb +10 -0
- data/lib/shopify_api/resources/province.rb +6 -0
- data/lib/shopify_api/resources/publication.rb +5 -0
- data/lib/shopify_api/resources/receipt.rb +5 -0
- data/lib/shopify_api/resources/recurring_application_charge.rb +34 -0
- data/lib/shopify_api/resources/redirect.rb +5 -0
- data/lib/shopify_api/resources/refund.rb +15 -0
- data/lib/shopify_api/resources/report.rb +5 -0
- data/lib/shopify_api/resources/resource_feedback.rb +19 -0
- data/lib/shopify_api/resources/rule.rb +5 -0
- data/lib/shopify_api/resources/script_tag.rb +5 -0
- data/lib/shopify_api/resources/shipping_address.rb +5 -0
- data/lib/shopify_api/resources/shipping_line.rb +5 -0
- data/lib/shopify_api/resources/shipping_rate.rb +7 -0
- data/lib/shopify_api/resources/shipping_zone.rb +5 -0
- data/lib/shopify_api/resources/shop.rb +26 -0
- data/lib/shopify_api/resources/smart_collection.rb +15 -0
- data/lib/shopify_api/resources/storefront_access_token.rb +5 -0
- data/lib/shopify_api/resources/tax_line.rb +5 -0
- data/lib/shopify_api/resources/tax_service.rb +5 -0
- data/lib/shopify_api/resources/tender_transaction.rb +6 -0
- data/lib/shopify_api/resources/theme.rb +5 -0
- data/lib/shopify_api/resources/transaction.rb +6 -0
- data/lib/shopify_api/resources/usage_charge.rb +6 -0
- data/lib/shopify_api/resources/user.rb +5 -0
- data/lib/shopify_api/resources/variant.rb +43 -0
- data/lib/shopify_api/resources/webhook.rb +5 -0
- data/lib/shopify_api/resources.rb +4 -0
- data/lib/shopify_api/session.rb +203 -0
- data/lib/shopify_api/version.rb +4 -0
- data/lib/shopify_api.rb +45 -0
- data/lib/verify_docs.rb +8 -0
- data/service.yml +2 -0
- data/shipit.rubygems.yml +1 -0
- data/shopify_api.gemspec +47 -0
- data/test/abandoned_checkouts_test.rb +29 -0
- data/test/access_scope_test.rb +23 -0
- data/test/access_token_test.rb +20 -0
- data/test/active_resource/json_errors_test.rb +19 -0
- data/test/api_access_test.rb +153 -0
- data/test/api_permission_test.rb +9 -0
- data/test/api_version_test.rb +157 -0
- data/test/application_charge_test.rb +82 -0
- data/test/application_credit_test.rb +36 -0
- data/test/article_test.rb +72 -0
- data/test/asset_test.rb +26 -0
- data/test/assigned_fulfillment_order_test.rb +78 -0
- data/test/base_test.rb +213 -0
- data/test/blog_test.rb +9 -0
- data/test/carrier_service_test.rb +18 -0
- data/test/cart_test.rb +14 -0
- data/test/checkouts_test.rb +77 -0
- data/test/collect_test.rb +10 -0
- data/test/collection_listing_test.rb +84 -0
- data/test/collection_publication_test.rb +40 -0
- data/test/collection_test.rb +50 -0
- data/test/countable_test.rb +14 -0
- data/test/currency_test.rb +21 -0
- data/test/custom_collection_test.rb +10 -0
- data/test/customer_saved_search_test.rb +37 -0
- data/test/customer_test.rb +58 -0
- data/test/detailed_log_subscriber_test.rb +143 -0
- data/test/discount_code_batch_test.rb +41 -0
- data/test/discount_code_test.rb +59 -0
- data/test/draft_order_test.rb +167 -0
- data/test/fixtures/abandoned_checkout.json +184 -0
- data/test/fixtures/abandoned_checkouts.json +186 -0
- data/test/fixtures/access_scopes.json +10 -0
- data/test/fixtures/access_token_delegate.json +4 -0
- data/test/fixtures/api_versions.json +38 -0
- data/test/fixtures/apis.json +42 -0
- data/test/fixtures/application_charge.json +16 -0
- data/test/fixtures/application_charges.json +57 -0
- data/test/fixtures/application_credit.json +12 -0
- data/test/fixtures/application_credits.json +24 -0
- data/test/fixtures/article.json +15 -0
- data/test/fixtures/articles.json +39 -0
- data/test/fixtures/asset.json +9 -0
- data/test/fixtures/assets.json +136 -0
- data/test/fixtures/assigned_fulfillment_orders.json +80 -0
- data/test/fixtures/authors.json +1 -0
- data/test/fixtures/blog.json +13 -0
- data/test/fixtures/blogs.json +13 -0
- data/test/fixtures/carrier_service.json +9 -0
- data/test/fixtures/carts.json +43 -0
- data/test/fixtures/checkout.json +160 -0
- data/test/fixtures/checkouts.json +162 -0
- data/test/fixtures/collect.json +12 -0
- data/test/fixtures/collection.json +17 -0
- data/test/fixtures/collection_listing.json +11 -0
- data/test/fixtures/collection_listing_product_ids.json +1 -0
- data/test/fixtures/collection_listing_product_ids2.json +1 -0
- data/test/fixtures/collection_listings.json +13 -0
- data/test/fixtures/collection_products.json +47 -0
- data/test/fixtures/collection_publication.json +11 -0
- data/test/fixtures/collection_publications.json +13 -0
- data/test/fixtures/currencies.json +25 -0
- data/test/fixtures/custom_collection.json +17 -0
- data/test/fixtures/customer_invite.json +9 -0
- data/test/fixtures/customer_saved_search.json +9 -0
- data/test/fixtures/customer_saved_search_customers.json +60 -0
- data/test/fixtures/customers.json +59 -0
- data/test/fixtures/customers_account_activation_url.json +3 -0
- data/test/fixtures/customers_search.json +60 -0
- data/test/fixtures/discount_code.json +10 -0
- data/test/fixtures/discount_code_batch.json +14 -0
- data/test/fixtures/discount_code_batch_discount_codes.json +21 -0
- data/test/fixtures/discount_codes.json +12 -0
- data/test/fixtures/draft_order.json +159 -0
- data/test/fixtures/draft_order_completed.json +159 -0
- data/test/fixtures/draft_order_invoice.json +9 -0
- data/test/fixtures/draft_orders.json +161 -0
- data/test/fixtures/engagement.json +15 -0
- data/test/fixtures/events.json +31 -0
- data/test/fixtures/fulfillment.json +49 -0
- data/test/fixtures/fulfillment_event.json +12 -0
- data/test/fixtures/fulfillment_order.json +39 -0
- data/test/fixtures/fulfillment_order_locations_for_move.json +18 -0
- data/test/fixtures/fulfillment_orders.json +80 -0
- data/test/fixtures/fulfillment_request.json +28 -0
- data/test/fixtures/fulfillment_service.json +10 -0
- data/test/fixtures/fulfillments.json +53 -0
- data/test/fixtures/gift_card.json +20 -0
- data/test/fixtures/gift_card_disabled.json +20 -0
- data/test/fixtures/graphql/2019-10.json +1083 -0
- data/test/fixtures/graphql/dummy_schema.rb +16 -0
- data/test/fixtures/graphql/unstable.json +1083 -0
- data/test/fixtures/image.json +10 -0
- data/test/fixtures/images.json +20 -0
- data/test/fixtures/inventory_level.json +7 -0
- data/test/fixtures/inventory_levels.json +24 -0
- data/test/fixtures/marketing_event.json +28 -0
- data/test/fixtures/marketing_events.json +54 -0
- data/test/fixtures/metafield.json +12 -0
- data/test/fixtures/metafields.json +34 -0
- data/test/fixtures/order.json +297 -0
- data/test/fixtures/order_risk.json +14 -0
- data/test/fixtures/order_risks.json +28 -0
- data/test/fixtures/order_with_properties.json +373 -0
- data/test/fixtures/orders.json +299 -0
- data/test/fixtures/payment.json +7 -0
- data/test/fixtures/payments.json +9 -0
- data/test/fixtures/ping/conversation.json +1 -0
- data/test/fixtures/ping/failed_delivery_confirmation.json +1 -0
- data/test/fixtures/ping/message.json +1 -0
- data/test/fixtures/ping/successful_delivery_confirmation.json +1 -0
- data/test/fixtures/policies.json +8 -0
- data/test/fixtures/price_rule.json +27 -0
- data/test/fixtures/price_rules.json +28 -0
- data/test/fixtures/product.json +116 -0
- data/test/fixtures/product_listing.json +86 -0
- data/test/fixtures/product_listing_product_ids.json +1 -0
- data/test/fixtures/product_listing_product_ids2.json +1 -0
- data/test/fixtures/product_listings.json +174 -0
- data/test/fixtures/product_publication.json +11 -0
- data/test/fixtures/product_publications.json +13 -0
- data/test/fixtures/publications.json +9 -0
- data/test/fixtures/recurring_application_charge.json +22 -0
- data/test/fixtures/recurring_application_charge_adjustment.json +5 -0
- data/test/fixtures/recurring_application_charges.json +106 -0
- data/test/fixtures/redirect.json +7 -0
- data/test/fixtures/refund.json +112 -0
- data/test/fixtures/report.json +9 -0
- data/test/fixtures/reports.json +11 -0
- data/test/fixtures/script_tag.json +10 -0
- data/test/fixtures/script_tags.json +18 -0
- data/test/fixtures/shipping_rates.json +12 -0
- data/test/fixtures/shipping_zones.json +315 -0
- data/test/fixtures/shop.json +26 -0
- data/test/fixtures/smart_collection.json +21 -0
- data/test/fixtures/smart_collection_products.json +155 -0
- data/test/fixtures/storefront_access_token.json +9 -0
- data/test/fixtures/storefront_access_tokens.json +18 -0
- data/test/fixtures/tags.json +1 -0
- data/test/fixtures/tax_service.json +9 -0
- data/test/fixtures/tender_transactions.json +52 -0
- data/test/fixtures/transaction.json +29 -0
- data/test/fixtures/usage_charge.json +11 -0
- data/test/fixtures/usage_charges.json +23 -0
- data/test/fixtures/user.json +21 -0
- data/test/fixtures/users.json +42 -0
- data/test/fixtures/variant.json +23 -0
- data/test/fixtures/variants.json +88 -0
- data/test/fixtures/webhook.json +10 -0
- data/test/fixtures/webhooks.json +18 -0
- data/test/fulfillment_event_test.rb +74 -0
- data/test/fulfillment_order_test.rb +530 -0
- data/test/fulfillment_order_test_helper.rb +8 -0
- data/test/fulfillment_request_test.rb +35 -0
- data/test/fulfillment_service_test.rb +18 -0
- data/test/fulfillment_test.rb +239 -0
- data/test/fulfillment_v2_test.rb +66 -0
- data/test/gift_card_test.rb +24 -0
- data/test/graphql/http_client_test.rb +26 -0
- data/test/graphql_test.rb +190 -0
- data/test/hmac_params_test.rb +25 -0
- data/test/image_test.rb +41 -0
- data/test/inventory_level_test.rb +68 -0
- data/test/lib/webmock_extensions/last_request.rb +16 -0
- data/test/limits_test.rb +39 -0
- data/test/location_test.rb +15 -0
- data/test/marketing_event_test.rb +68 -0
- data/test/message_enricher_test.rb +45 -0
- data/test/meta_test.rb +47 -0
- data/test/metafield_test.rb +56 -0
- data/test/order_risk_test.rb +47 -0
- data/test/order_test.rb +140 -0
- data/test/pagination_test.rb +290 -0
- data/test/payment_test.rb +19 -0
- data/test/policy_test.rb +20 -0
- data/test/price_rule_test.rb +70 -0
- data/test/product_listing_test.rb +97 -0
- data/test/product_publication_test.rb +40 -0
- data/test/product_test.rb +111 -0
- data/test/publication_test.rb +12 -0
- data/test/recurring_application_charge_test.rb +204 -0
- data/test/redirect_test.rb +10 -0
- data/test/refund_test.rb +37 -0
- data/test/report_test.rb +37 -0
- data/test/resource_feedback_test.rb +43 -0
- data/test/script_tag_test.rb +31 -0
- data/test/session_test.rb +640 -0
- data/test/shipping_rate_test.rb +17 -0
- data/test/shipping_zone_test.rb +11 -0
- data/test/shop_test.rb +82 -0
- data/test/smart_collection_test.rb +11 -0
- data/test/storefront_access_token_test.rb +30 -0
- data/test/tax_service_test.rb +12 -0
- data/test/tender_transaction_test.rb +18 -0
- data/test/test_helper.rb +126 -0
- data/test/transaction_test.rb +18 -0
- data/test/usage_charge_test.rb +25 -0
- data/test/user_test.rb +18 -0
- data/test/variant_test.rb +73 -0
- data/test/webhook_test.rb +24 -0
- metadata +555 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShopifyAPI
|
|
4
|
+
module HmacParams
|
|
5
|
+
class << self
|
|
6
|
+
def encode(params)
|
|
7
|
+
params
|
|
8
|
+
.except(:signature, :hmac, :action, :controller)
|
|
9
|
+
.map { |k,v| sprintf("%s=%s", encode_key(k), encode_value(v)) }
|
|
10
|
+
.sort.join("&")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
KEY_REGEXP = /([#{Regexp.escape("&=%")}])/n
|
|
14
|
+
def encode_key(key)
|
|
15
|
+
_escape(key.to_s, KEY_REGEXP)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
VALUE_REGEXP = /([#{Regexp.escape("&%")}])/n
|
|
19
|
+
def encode_value(value)
|
|
20
|
+
_escape(value.to_s, VALUE_REGEXP)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def _escape(str, regex)
|
|
26
|
+
str = str.b
|
|
27
|
+
str.gsub!(regex) {"%%%02X" % $1.ord}
|
|
28
|
+
# %-escaped string should contain US-ASCII only
|
|
29
|
+
str.force_encoding(Encoding::US_ASCII)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyAPI
|
|
3
|
+
module Limits
|
|
4
|
+
class LimitUnavailable < StandardError; end
|
|
5
|
+
|
|
6
|
+
def self.included(klass)
|
|
7
|
+
klass.send(:extend, ClassMethods)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
# Takes form <call count>/<bucket size>
|
|
12
|
+
# See https://help.shopify.com/en/api/getting-started/api-call-limit
|
|
13
|
+
# Eg: 2/40
|
|
14
|
+
CREDIT_LIMIT_HEADER_PARAM = {
|
|
15
|
+
shop: 'X-Shopify-Shop-Api-Call-Limit',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# How many more API calls can I make?
|
|
20
|
+
# @return {Integer}
|
|
21
|
+
#
|
|
22
|
+
def credit_left
|
|
23
|
+
credit_limit(:shop) - credit_used(:shop)
|
|
24
|
+
end
|
|
25
|
+
alias_method :available_calls, :credit_left
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
# Have I reached my API call limit?
|
|
29
|
+
# @return {Boolean}
|
|
30
|
+
#
|
|
31
|
+
def credit_maxed?
|
|
32
|
+
credit_left <= 0
|
|
33
|
+
end
|
|
34
|
+
alias_method :maxed?, :credit_maxed?
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# How many total API calls can I make?
|
|
38
|
+
# NOTE: subtracting 1 from credit_limit because I think ShopifyAPI cuts off at 299 or shop limits.
|
|
39
|
+
# @param {Symbol} scope [:shop]
|
|
40
|
+
# @return {Integer}
|
|
41
|
+
#
|
|
42
|
+
def credit_limit(scope = :shop)
|
|
43
|
+
api_credit_limit_param(scope).pop.to_i - 1
|
|
44
|
+
end
|
|
45
|
+
alias_method :call_limit, :credit_limit
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# How many API calls have I made?
|
|
49
|
+
# @param {Symbol} scope [:shop]
|
|
50
|
+
# @return {Integer}
|
|
51
|
+
#
|
|
52
|
+
def credit_used(scope = :shop)
|
|
53
|
+
api_credit_limit_param(scope).shift.to_i
|
|
54
|
+
end
|
|
55
|
+
alias_method :call_count, :credit_used
|
|
56
|
+
|
|
57
|
+
##
|
|
58
|
+
# @return {HTTPResonse}
|
|
59
|
+
#
|
|
60
|
+
def response
|
|
61
|
+
Shop.current unless ShopifyAPI::Base.connection.response
|
|
62
|
+
ShopifyAPI::Base.connection.response
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
##
|
|
68
|
+
# @return {Array}
|
|
69
|
+
#
|
|
70
|
+
def api_credit_limit_param(scope)
|
|
71
|
+
header = response[CREDIT_LIMIT_HEADER_PARAM[scope]]
|
|
72
|
+
raise LimitUnavailable unless header
|
|
73
|
+
header.split('/')
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyAPI
|
|
3
|
+
class MessageEnricher < SimpleDelegator
|
|
4
|
+
def message
|
|
5
|
+
return super unless (400...500).include?(code.to_i)
|
|
6
|
+
|
|
7
|
+
@_cached_message ||= begin
|
|
8
|
+
detailed_error =
|
|
9
|
+
begin
|
|
10
|
+
parsed_body = JSON.parse(body)
|
|
11
|
+
|
|
12
|
+
if parsed_body['error']
|
|
13
|
+
parsed_body['error'].to_s
|
|
14
|
+
elsif parsed_body['errors']
|
|
15
|
+
Array(parsed_body['errors']).join('; ')
|
|
16
|
+
end
|
|
17
|
+
rescue JSON::ParserError
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
detailed_error.present? ? "#{super} (#{detailed_error})" : super
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
module ShopifyAPI
|
|
4
|
+
class Meta < ActiveResource::Base
|
|
5
|
+
self.site = "https://app.shopify.com/services/"
|
|
6
|
+
self.element_name = 'api'
|
|
7
|
+
self.primary_key = :handle
|
|
8
|
+
self.timeout = 5
|
|
9
|
+
|
|
10
|
+
def self.admin_versions
|
|
11
|
+
all.find { |api| api.handle = :admin }.versions
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyAPI
|
|
3
|
+
module Metafields
|
|
4
|
+
def metafields(**options)
|
|
5
|
+
options.merge!(resource: self.class.collection_name, resource_id: id)
|
|
6
|
+
|
|
7
|
+
Metafield.find(:all, params: options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def add_metafield(metafield)
|
|
11
|
+
raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
|
|
12
|
+
|
|
13
|
+
metafield.prefix_options = {
|
|
14
|
+
resource: self.class.collection_name,
|
|
15
|
+
resource_id: id,
|
|
16
|
+
}
|
|
17
|
+
metafield.save
|
|
18
|
+
metafield
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShopifyAPI
|
|
4
|
+
class PaginatedCollection < ActiveResource::Collection
|
|
5
|
+
module CollectionPagination
|
|
6
|
+
def initialize(args)
|
|
7
|
+
@next_url = pagination_link_headers.next_link&.url&.to_s
|
|
8
|
+
@previous_url = pagination_link_headers.previous_link&.url&.to_s
|
|
9
|
+
super(args)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def next_page?
|
|
13
|
+
ensure_available
|
|
14
|
+
@next_url.present?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def previous_page?
|
|
18
|
+
ensure_available
|
|
19
|
+
@previous_url.present?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def fetch_next_page
|
|
23
|
+
fetch_page(@next_url)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def fetch_previous_page
|
|
27
|
+
fetch_page(@previous_url)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def next_page_info
|
|
31
|
+
extract_page_info(@next_url)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def previous_page_info
|
|
35
|
+
extract_page_info(@previous_url)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
AVAILABLE_IN_VERSION = ShopifyAPI::ApiVersion.find_version('2019-10')
|
|
41
|
+
AVAILABLE_IN_VERSION_EARLY = ShopifyAPI::ApiVersion.find_version('2019-07')
|
|
42
|
+
|
|
43
|
+
def fetch_page(url)
|
|
44
|
+
ensure_available
|
|
45
|
+
return [] unless url.present?
|
|
46
|
+
|
|
47
|
+
resource_class.all(from: url)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def pagination_link_headers
|
|
51
|
+
@pagination_link_headers ||= ShopifyAPI::PaginationLinkHeaders.new(
|
|
52
|
+
ShopifyAPI::Base.connection.response["Link"]
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def ensure_available
|
|
57
|
+
return if ShopifyAPI::Base.api_version >= AVAILABLE_IN_VERSION
|
|
58
|
+
return if ShopifyAPI::Base.api_version >= AVAILABLE_IN_VERSION_EARLY && resource_class.early_july_pagination?
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def extract_page_info(url)
|
|
63
|
+
CGI.escape(Rack::Utils.parse_query(URI(url).query)['page_info']) if url.present?
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
include CollectionPagination
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyAPI
|
|
3
|
+
class InvalidPaginationLinksError < StandardError; end
|
|
4
|
+
|
|
5
|
+
class PaginationLinkHeaders
|
|
6
|
+
LinkHeader = Struct.new(:url, :rel)
|
|
7
|
+
attr_reader :previous_link, :next_link
|
|
8
|
+
|
|
9
|
+
def initialize(link_header)
|
|
10
|
+
links = parse_link_header(link_header)
|
|
11
|
+
@previous_link = links.find { |link| link.rel == :previous }
|
|
12
|
+
@next_link = links.find { |link| link.rel == :next }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def parse_link_header(link_header)
|
|
18
|
+
return [] unless link_header.present?
|
|
19
|
+
links = link_header.split(',')
|
|
20
|
+
links.map do |link|
|
|
21
|
+
parts = link.split('; ')
|
|
22
|
+
unless parts.length == 2
|
|
23
|
+
raise ShopifyAPI::InvalidPaginationLinksError, "Invalid link header: url and rel expected"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
url = parts[0][/<(.*)>/, 1]
|
|
27
|
+
rel = parts[1][/rel="(.*)"/, 1]&.to_sym
|
|
28
|
+
|
|
29
|
+
url = URI.parse(url).request_uri
|
|
30
|
+
LinkHeader.new(url, rel)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyAPI
|
|
3
|
+
class AccessToken < Base
|
|
4
|
+
def self.delegate(access_scope, expires_in = nil)
|
|
5
|
+
resource = post(:delegate, delegate_access_scope: access_scope, expires_in: expires_in)
|
|
6
|
+
instantiate_record(format.decode(resource.body), {})
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyAPI
|
|
3
|
+
class ApplicationCharge < Base
|
|
4
|
+
undef_method :test
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
[:pending, :expired, :accepted, :declined].each do |status|
|
|
8
|
+
define_method(status) { (all || []).select { |c| c.status == status.to_s } }
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def activate
|
|
13
|
+
load_attributes_from_response(post(:activate))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyAPI
|
|
3
|
+
class Article < Base
|
|
4
|
+
include Events
|
|
5
|
+
include Metafields
|
|
6
|
+
include DisablePrefixCheck
|
|
7
|
+
|
|
8
|
+
conditional_prefix :blog
|
|
9
|
+
|
|
10
|
+
def comments
|
|
11
|
+
Comment.find(:all, params: { article_id: id })
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.authors(options = {})
|
|
15
|
+
get(:authors, options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.tags(options = {})
|
|
19
|
+
get(:tags, options)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyAPI
|
|
3
|
+
# Assets represent the files that comprise your theme.
|
|
4
|
+
# There are different buckets which hold different kinds
|
|
5
|
+
# of assets, each corresponding to one of the folders
|
|
6
|
+
# within a theme's zip file: "layout", "templates",
|
|
7
|
+
# "snippets", "assets", and "config". The full key of an
|
|
8
|
+
# asset always starts with the bucket name, and the path
|
|
9
|
+
# separator is a forward slash, like layout/theme.liquid
|
|
10
|
+
# or assets/bg-body.gif.
|
|
11
|
+
#
|
|
12
|
+
# Initialize with a key:
|
|
13
|
+
# asset = ShopifyAPI::Asset.new(key: 'assets/special.css', theme_id: 12345)
|
|
14
|
+
#
|
|
15
|
+
# Find by key:
|
|
16
|
+
# asset = ShopifyAPI::Asset.find('assets/image.png', params: {theme_id: 12345})
|
|
17
|
+
#
|
|
18
|
+
# Get the text or binary value:
|
|
19
|
+
# asset.value # decodes from attachment attribute if necessary
|
|
20
|
+
#
|
|
21
|
+
# You can provide new data for assets in a few different ways:
|
|
22
|
+
#
|
|
23
|
+
# * assign text data for the value directly:
|
|
24
|
+
# asset.value = "div.special {color:red;}"
|
|
25
|
+
#
|
|
26
|
+
# * provide binary data for the value:
|
|
27
|
+
# asset.attach(File.read('image.png'))
|
|
28
|
+
#
|
|
29
|
+
# * set a URL from which Shopify will fetch the value:
|
|
30
|
+
# asset.src = "http://mysite.com/image.png"
|
|
31
|
+
#
|
|
32
|
+
# * set a source key of another of your assets from which
|
|
33
|
+
# the value will be copied:
|
|
34
|
+
# asset.source_key = "assets/another_image.png"
|
|
35
|
+
class Asset < Base
|
|
36
|
+
include DisablePrefixCheck
|
|
37
|
+
|
|
38
|
+
self.primary_key = 'key'
|
|
39
|
+
|
|
40
|
+
conditional_prefix :theme
|
|
41
|
+
|
|
42
|
+
def self.element_path(_id, prefix_options = {}, query_options = nil) #:nodoc:
|
|
43
|
+
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
|
|
44
|
+
"#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# find an asset by key:
|
|
48
|
+
# ShopifyAPI::Asset.find('layout/theme.liquid', params: { theme_id: 99 })
|
|
49
|
+
def self.find(*args)
|
|
50
|
+
if args[0].is_a?(Symbol)
|
|
51
|
+
super
|
|
52
|
+
else
|
|
53
|
+
params = { asset: { key: args[0] } }
|
|
54
|
+
params = params.merge(args[1][:params]) if args[1] && args[1][:params]
|
|
55
|
+
path_prefix = params[:theme_id] ? "themes/#{params[:theme_id]}/" : ""
|
|
56
|
+
resource = find(
|
|
57
|
+
:one,
|
|
58
|
+
from: api_version.construct_api_path("#{path_prefix}assets.#{format.extension}"),
|
|
59
|
+
params: params
|
|
60
|
+
)
|
|
61
|
+
resource.prefix_options[:theme_id] = params[:theme_id] if resource && params[:theme_id]
|
|
62
|
+
resource
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# For text assets, Shopify returns the data in the 'value' attribute.
|
|
67
|
+
# For binary assets, the data is base-64-encoded and returned in the
|
|
68
|
+
# 'attachment' attribute. This accessor returns the data in both cases.
|
|
69
|
+
def value
|
|
70
|
+
attributes['value'] ||
|
|
71
|
+
(attributes['attachment'] ? Base64.decode64(attributes['attachment']) : nil)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def attach(data)
|
|
75
|
+
self.attachment = Base64.encode64(data)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def destroy
|
|
79
|
+
connection.delete(element_path(prefix_options.merge(asset: { key: key })), self.class.headers)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def new?
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def method_missing(method_symbol, *arguments) #:nodoc:
|
|
87
|
+
if %w{value= attachment= src= source_key=}.include?(method_symbol)
|
|
88
|
+
wipe_value_attributes
|
|
89
|
+
end
|
|
90
|
+
super
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def wipe_value_attributes
|
|
96
|
+
%w{value attachment src source_key}.each do |attr|
|
|
97
|
+
attributes.delete(attr)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyAPI
|
|
3
|
+
class AssignedFulfillmentOrder < Base
|
|
4
|
+
def initialize(attributes = {}, persisted = false)
|
|
5
|
+
ShopifyAPI::Base.version_validation!(FulfillmentOrder::MINIMUM_VERSION)
|
|
6
|
+
super(attributes, persisted)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.find(scope, *args)
|
|
10
|
+
ShopifyAPI::Base.version_validation!(FulfillmentOrder::MINIMUM_VERSION)
|
|
11
|
+
|
|
12
|
+
assigned_fulfillment_orders = super(scope, *args)
|
|
13
|
+
assigned_fulfillment_orders.map { |afo| FulfillmentOrder.new(afo.attributes) }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'shopify_api/version'
|
|
3
|
+
|
|
4
|
+
module ShopifyAPI
|
|
5
|
+
class Base < ActiveResource::Base
|
|
6
|
+
class InvalidSessionError < StandardError; end
|
|
7
|
+
extend Countable
|
|
8
|
+
|
|
9
|
+
self.timeout = 90
|
|
10
|
+
self.include_root_in_json = false
|
|
11
|
+
self.headers['User-Agent'] = ["RubyShopifyAPI/#{ShopifyAPI::VERSION}",
|
|
12
|
+
"ActiveResource/#{ActiveResource::VERSION::STRING}",
|
|
13
|
+
"Ruby/#{RUBY_VERSION}"].join(' ')
|
|
14
|
+
|
|
15
|
+
self.collection_parser = ShopifyAPI::PaginatedCollection
|
|
16
|
+
|
|
17
|
+
def encode(options = {})
|
|
18
|
+
same = dup
|
|
19
|
+
same.attributes = { self.class.element_name => same.attributes } if self.class.format.extension == 'json'
|
|
20
|
+
|
|
21
|
+
same.send("to_#{self.class.format.extension}", options)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def as_json(options = nil)
|
|
25
|
+
root = options[:root] if options.try(:key?, :root)
|
|
26
|
+
if include_root_in_json
|
|
27
|
+
root = self.class.model_name.element if root == true
|
|
28
|
+
{ root => serializable_hash(options) }
|
|
29
|
+
else
|
|
30
|
+
serializable_hash(options)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class << self
|
|
35
|
+
threadsafe_attribute(:_api_version)
|
|
36
|
+
def headers
|
|
37
|
+
if _headers_defined?
|
|
38
|
+
_headers
|
|
39
|
+
elsif superclass != Object && superclass.headers
|
|
40
|
+
superclass.headers
|
|
41
|
+
else
|
|
42
|
+
_headers ||= {}
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def activate_session(session)
|
|
47
|
+
raise InvalidSessionError, "Session cannot be nil" if session.nil?
|
|
48
|
+
self.site = session.site
|
|
49
|
+
self.headers.merge!('X-Shopify-Access-Token' => session.token)
|
|
50
|
+
self.api_version = session.api_version
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def clear_session
|
|
54
|
+
self.site = nil
|
|
55
|
+
self.password = nil
|
|
56
|
+
self.user = nil
|
|
57
|
+
self.headers.delete('X-Shopify-Access-Token')
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def api_version
|
|
61
|
+
if _api_version_defined?
|
|
62
|
+
_api_version
|
|
63
|
+
elsif superclass != Object && superclass.site
|
|
64
|
+
superclass.api_version.dup.freeze
|
|
65
|
+
else
|
|
66
|
+
ApiVersion::NullVersion
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def api_version=(version)
|
|
71
|
+
self._api_version = if ApiVersion::NullVersion.matches?(version)
|
|
72
|
+
ApiVersion::NullVersion
|
|
73
|
+
else
|
|
74
|
+
ApiVersion.find_version(version)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def prefix(options = {})
|
|
79
|
+
api_version.construct_api_path(resource_prefix(options))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def prefix_source
|
|
83
|
+
''
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def resource_prefix(_options = {})
|
|
87
|
+
''
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Sets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.json</tt>).
|
|
91
|
+
# Default value is <tt>site.path</tt>.
|
|
92
|
+
def resource_prefix=(value)
|
|
93
|
+
@prefix_parameters = nil
|
|
94
|
+
|
|
95
|
+
resource_prefix_call = value.gsub(/:\w+/) { |key| "\#{URI::DEFAULT_PARSER.escape options[#{key}].to_s}" }
|
|
96
|
+
|
|
97
|
+
silence_warnings do
|
|
98
|
+
# Redefine the new methods.
|
|
99
|
+
instance_eval(<<-RUBY_EVAL, __FILE__, __LINE__ + 1)
|
|
100
|
+
def prefix_source() "#{value}" end
|
|
101
|
+
def resource_prefix(options={}) "#{resource_prefix_call}" end
|
|
102
|
+
RUBY_EVAL
|
|
103
|
+
end
|
|
104
|
+
rescue => e
|
|
105
|
+
logger&.error("Couldn't set prefix: #{e}\n #{code}")
|
|
106
|
+
raise
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def prefix=(value)
|
|
110
|
+
if value.start_with?('/admin')
|
|
111
|
+
raise ArgumentError, "'#{value}' can no longer start /admin/. Change to using resource_prefix="
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
warn(
|
|
115
|
+
'[DEPRECATED] ShopifyAPI::Base#prefix= is deprecated and will be removed in a future version. ' \
|
|
116
|
+
'Use `self.resource_prefix=` instead.'
|
|
117
|
+
)
|
|
118
|
+
self.resource_prefix = value
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
alias_method :set_prefix, :prefix=
|
|
122
|
+
|
|
123
|
+
def init_prefix(resource)
|
|
124
|
+
init_prefix_explicit(resource.to_s.pluralize, "#{resource}_id")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def init_prefix_explicit(resource_type, resource_id)
|
|
128
|
+
self.resource_prefix = "#{resource_type}/:#{resource_id}/"
|
|
129
|
+
|
|
130
|
+
define_method(resource_id.to_sym) do
|
|
131
|
+
@prefix_options[resource_id]
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def early_july_pagination?
|
|
136
|
+
!!early_july_pagination
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def version_validation!(minimum_version)
|
|
140
|
+
available_in_version = ShopifyAPI::ApiVersion.find_version(minimum_version)
|
|
141
|
+
|
|
142
|
+
unless ShopifyAPI::Base.api_version >= available_in_version
|
|
143
|
+
raise NotImplementedError, "The minimum supported version is #{minimum_version}."
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
attr_accessor :early_july_pagination
|
|
150
|
+
|
|
151
|
+
def early_july_pagination_release!
|
|
152
|
+
self.early_july_pagination = true
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def persisted?
|
|
157
|
+
!id.nil?
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
def only_id
|
|
163
|
+
encode(only: :id, include: [], methods: [])
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|