shopify_api 4.9.0 → 9.4.1
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 +5 -5
- data/.github/CODEOWNERS +1 -0
- data/.github/ISSUE_TEMPLATE.md +36 -0
- data/.github/probots.yml +2 -0
- data/.github/workflows/build.yml +41 -0
- data/.gitignore +5 -1
- data/.rubocop.yml +28 -0
- data/.rubocop_todo.yml +75 -0
- data/CHANGELOG.md +491 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +6 -2
- data/Gemfile.lock +151 -0
- data/Gemfile_ar41 +5 -0
- data/Gemfile_ar50 +5 -0
- data/Gemfile_ar51 +5 -0
- data/Gemfile_ar_master +0 -1
- data/README.md +492 -100
- data/RELEASING +10 -9
- data/Rakefile +21 -5
- data/SECURITY.md +59 -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 +1 -0
- data/lib/active_resource/detailed_log_subscriber.rb +43 -7
- data/lib/active_resource/json_errors.rb +8 -2
- data/lib/shopify_api.rb +16 -6
- data/lib/shopify_api/api_access.rb +57 -0
- data/lib/shopify_api/api_version.rb +206 -0
- data/lib/shopify_api/connection.rb +7 -4
- data/lib/shopify_api/countable.rb +3 -2
- data/lib/shopify_api/disable_prefix_check.rb +31 -0
- data/lib/shopify_api/events.rb +2 -1
- data/lib/shopify_api/graphql.rb +103 -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/limits.rb +9 -9
- data/lib/shopify_api/message_enricher.rb +25 -0
- data/lib/shopify_api/meta.rb +14 -0
- data/lib/shopify_api/metafields.rb +5 -4
- data/lib/shopify_api/paginated_collection.rb +69 -0
- data/lib/shopify_api/pagination_link_headers.rb +34 -0
- data/lib/shopify_api/resources.rb +3 -1
- 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 +1 -0
- data/lib/shopify_api/resources/address.rb +1 -0
- data/lib/shopify_api/resources/announcement.rb +2 -1
- data/lib/shopify_api/resources/api_permission.rb +9 -0
- data/lib/shopify_api/resources/application_charge.rb +1 -0
- data/lib/shopify_api/resources/application_credit.rb +1 -0
- data/lib/shopify_api/resources/array_base.rb +13 -0
- data/lib/shopify_api/resources/article.rb +3 -2
- data/lib/shopify_api/resources/asset.rb +28 -23
- data/lib/shopify_api/resources/assigned_fulfillment_order.rb +16 -0
- data/lib/shopify_api/resources/base.rb +101 -24
- data/lib/shopify_api/resources/billing_address.rb +2 -1
- data/lib/shopify_api/resources/blog.rb +2 -1
- data/lib/shopify_api/resources/carrier_service.rb +1 -0
- data/lib/shopify_api/resources/cart.rb +2 -1
- data/lib/shopify_api/resources/checkout.rb +27 -1
- data/lib/shopify_api/resources/collect.rb +2 -0
- data/lib/shopify_api/resources/collection.rb +14 -0
- data/lib/shopify_api/resources/collection_listing.rb +11 -1
- data/lib/shopify_api/resources/collection_publication.rb +10 -0
- data/lib/shopify_api/resources/comment.rb +20 -5
- data/lib/shopify_api/resources/country.rb +1 -0
- data/lib/shopify_api/resources/currency.rb +6 -0
- data/lib/shopify_api/resources/custom_collection.rb +7 -6
- data/lib/shopify_api/resources/customer.rb +2 -1
- data/lib/shopify_api/resources/customer_group.rb +1 -0
- data/lib/shopify_api/resources/{customer_invite_message.rb → customer_invite.rb} +1 -0
- data/lib/shopify_api/resources/customer_saved_search.rb +4 -1
- data/lib/shopify_api/resources/discount_code.rb +1 -0
- data/lib/shopify_api/resources/discount_code_batch.rb +34 -0
- data/lib/shopify_api/resources/draft_order.rb +1 -0
- data/lib/shopify_api/resources/draft_order_invoice.rb +1 -0
- data/lib/shopify_api/resources/event.rb +2 -0
- data/lib/shopify_api/resources/fulfillment.rb +46 -3
- data/lib/shopify_api/resources/fulfillment_event.rb +2 -1
- 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 +1 -0
- data/lib/shopify_api/resources/fulfillment_service.rb +1 -0
- data/lib/shopify_api/resources/fulfillment_v2.rb +21 -0
- data/lib/shopify_api/resources/gift_card.rb +1 -0
- data/lib/shopify_api/resources/image.rb +4 -3
- 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 +10 -1
- data/lib/shopify_api/resources/location.rb +4 -0
- data/lib/shopify_api/resources/marketing_event.rb +3 -0
- data/lib/shopify_api/resources/metafield.rb +2 -0
- data/lib/shopify_api/resources/note_attribute.rb +1 -0
- data/lib/shopify_api/resources/option.rb +1 -0
- data/lib/shopify_api/resources/order.rb +25 -5
- data/lib/shopify_api/resources/order_risk.rb +1 -0
- data/lib/shopify_api/resources/page.rb +1 -0
- data/lib/shopify_api/resources/payment.rb +7 -0
- data/lib/shopify_api/resources/payment_details.rb +1 -0
- data/lib/shopify_api/resources/ping.rb +3 -0
- data/lib/shopify_api/resources/policy.rb +1 -0
- data/lib/shopify_api/resources/price_rule.rb +1 -1
- data/lib/shopify_api/resources/product.rb +33 -7
- data/lib/shopify_api/resources/product_listing.rb +9 -1
- data/lib/shopify_api/resources/product_publication.rb +10 -0
- data/lib/shopify_api/resources/province.rb +1 -0
- data/lib/shopify_api/resources/publication.rb +5 -0
- data/lib/shopify_api/resources/receipt.rb +1 -0
- data/lib/shopify_api/resources/recurring_application_charge.rb +4 -1
- data/lib/shopify_api/resources/redirect.rb +1 -0
- data/lib/shopify_api/resources/refund.rb +6 -4
- data/lib/shopify_api/resources/report.rb +1 -0
- data/lib/shopify_api/resources/resource_feedback.rb +1 -1
- data/lib/shopify_api/resources/rule.rb +1 -0
- data/lib/shopify_api/resources/script_tag.rb +1 -0
- data/lib/shopify_api/resources/shipping_address.rb +1 -0
- data/lib/shopify_api/resources/shipping_line.rb +2 -1
- data/lib/shopify_api/resources/shipping_rate.rb +7 -0
- data/lib/shopify_api/resources/shipping_zone.rb +1 -0
- data/lib/shopify_api/resources/shop.rb +10 -7
- data/lib/shopify_api/resources/smart_collection.rb +3 -3
- data/lib/shopify_api/resources/storefront_access_token.rb +1 -0
- data/lib/shopify_api/resources/tax_line.rb +1 -0
- data/lib/shopify_api/resources/tax_service.rb +1 -0
- data/lib/shopify_api/resources/tender_transaction.rb +6 -0
- data/lib/shopify_api/resources/theme.rb +1 -0
- data/lib/shopify_api/resources/transaction.rb +1 -0
- data/lib/shopify_api/resources/usage_charge.rb +1 -0
- data/lib/shopify_api/resources/user.rb +1 -0
- data/lib/shopify_api/resources/variant.rb +35 -0
- data/lib/shopify_api/resources/webhook.rb +1 -0
- data/lib/shopify_api/session.rb +109 -45
- data/lib/shopify_api/version.rb +2 -1
- data/lib/verify_docs.rb +8 -0
- data/service.yml +8 -0
- data/shopify_api.gemspec +19 -8
- data/test/abandoned_checkouts_test.rb +29 -0
- data/test/access_scope_test.rb +23 -0
- data/test/access_token_test.rb +6 -5
- data/test/active_resource/json_errors_test.rb +6 -6
- 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 +30 -27
- data/test/application_credit_test.rb +10 -9
- data/test/article_test.rb +34 -35
- data/test/asset_test.rb +14 -6
- data/test/assigned_fulfillment_order_test.rb +78 -0
- data/test/base_test.rb +147 -59
- data/test/blog_test.rb +4 -3
- data/test/carrier_service_test.rb +7 -6
- data/test/cart_test.rb +2 -1
- data/test/checkouts_test.rb +72 -4
- data/test/collect_test.rb +4 -3
- data/test/collection_listing_test.rb +56 -13
- data/test/collection_publication_test.rb +40 -0
- data/test/collection_test.rb +50 -0
- data/test/countable_test.rb +3 -2
- data/test/currency_test.rb +21 -0
- data/test/custom_collection_test.rb +4 -3
- data/test/customer_saved_search_test.rb +18 -8
- data/test/customer_test.rb +22 -14
- data/test/detailed_log_subscriber_test.rb +113 -19
- data/test/discount_code_batch_test.rb +41 -0
- data/test/discount_code_test.rb +22 -16
- data/test/draft_order_test.rb +68 -52
- 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/api_versions.json +38 -0
- data/test/fixtures/apis.json +42 -0
- data/test/fixtures/assigned_fulfillment_orders.json +80 -0
- data/test/fixtures/checkout.json +160 -0
- data/test/fixtures/checkouts.json +25 -49
- data/test/fixtures/collection.json +17 -0
- data/test/fixtures/collection_listing_product_ids2.json +1 -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/discount_code_batch.json +14 -0
- data/test/fixtures/discount_code_batch_discount_codes.json +21 -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/fulfillments.json +53 -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/inventory_level.json +7 -0
- data/test/fixtures/inventory_levels.json +24 -0
- data/test/fixtures/order_with_properties.json +373 -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/product_listing_product_ids.json +1 -1
- data/test/fixtures/product_listing_product_ids2.json +1 -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/shipping_rates.json +12 -0
- data/test/fixtures/smart_collection_products.json +155 -0
- data/test/fixtures/tender_transactions.json +52 -0
- data/test/fulfillment_event_test.rb +31 -26
- data/test/fulfillment_order_test.rb +530 -0
- data/test/fulfillment_order_test_helper.rb +8 -0
- data/test/fulfillment_request_test.rb +10 -8
- data/test/fulfillment_service_test.rb +13 -12
- data/test/fulfillment_test.rb +198 -20
- data/test/fulfillment_v2_test.rb +66 -0
- data/test/gift_card_test.rb +6 -4
- data/test/graphql/http_client_test.rb +26 -0
- data/test/graphql_test.rb +190 -0
- data/test/image_test.rb +19 -17
- data/test/inventory_level_test.rb +68 -0
- data/test/lib/webmock_extensions/last_request.rb +16 -0
- data/test/limits_test.rb +4 -3
- data/test/location_test.rb +15 -0
- data/test/marketing_event_test.rb +21 -21
- data/test/message_enricher_test.rb +45 -0
- data/test/meta_test.rb +47 -0
- data/test/metafield_test.rb +30 -20
- data/test/order_risk_test.rb +17 -16
- data/test/order_test.rb +110 -17
- data/test/pagination_test.rb +290 -0
- data/test/payment_test.rb +19 -0
- data/test/policy_test.rb +6 -5
- data/test/price_rule_test.rb +20 -15
- data/test/product_listing_test.rb +72 -15
- data/test/product_publication_test.rb +40 -0
- data/test/product_test.rb +80 -19
- data/test/publication_test.rb +12 -0
- data/test/recurring_application_charge_test.rb +105 -50
- data/test/redirect_test.rb +4 -3
- data/test/refund_test.rb +22 -17
- data/test/report_test.rb +12 -10
- data/test/resource_feedback_test.rb +14 -13
- data/test/script_tag_test.rb +10 -9
- data/test/session_test.rb +497 -111
- data/test/shipping_rate_test.rb +17 -0
- data/test/shipping_zone_test.rb +4 -3
- data/test/shop_test.rb +47 -33
- data/test/smart_collection_test.rb +5 -4
- data/test/storefront_access_token_test.rb +13 -15
- data/test/tax_service_test.rb +7 -3
- data/test/tender_transaction_test.rb +18 -0
- data/test/test_helper.rb +98 -67
- data/test/transaction_test.rb +4 -3
- data/test/usage_charge_test.rb +12 -8
- data/test/user_test.rb +4 -3
- data/test/variant_test.rb +50 -20
- data/test/webhook_test.rb +10 -7
- metadata +196 -37
- data/.travis.yml +0 -36
- data/CHANGELOG +0 -292
- data/Gemfile_ar30 +0 -6
- data/Gemfile_ar31 +0 -6
- data/Gemfile_ar32 +0 -6
- data/Gemfile_ar40 +0 -6
- data/bin/shopify +0 -3
- data/lib/active_resource/base_ext.rb +0 -21
- data/lib/active_resource/disable_prefix_check.rb +0 -36
- data/lib/active_resource/to_query.rb +0 -10
- data/lib/shopify_api/json_format.rb +0 -18
- data/lib/shopify_api/resources/discount.rb +0 -11
- data/lib/shopify_api/resources/o_auth.rb +0 -9
- data/test/discount_test.rb +0 -52
- data/test/fixtures/discount.json +0 -17
- data/test/fixtures/discount_disabled.json +0 -17
- data/test/fixtures/discounts.json +0 -34
- data/test/fixtures/o_auth_revoke.json +0 -5
- data/test/o_auth_test.rb +0 -8
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyAPI
|
|
3
|
+
module DisablePrefixCheck
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
module ClassMethods
|
|
7
|
+
def check_prefix_options(options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# `flexible = true` is hack to allow multiple things through the same AR class
|
|
11
|
+
def conditional_prefix(resource, flexible = false)
|
|
12
|
+
resource_id = "#{resource}_id".to_sym
|
|
13
|
+
resource_type = flexible ? ":#{resource}" : resource.to_s.pluralize
|
|
14
|
+
|
|
15
|
+
init_prefix_explicit(resource_type, resource_id)
|
|
16
|
+
|
|
17
|
+
define_singleton_method(:resource_prefix) do |options = {}|
|
|
18
|
+
resource_type = options[resource] if flexible
|
|
19
|
+
|
|
20
|
+
options[resource_id].nil? ? '' : "#{resource_type}/#{options[resource_id]}/"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
define_singleton_method(:instantiate_record) do |record, prefix_options = {}|
|
|
24
|
+
new(record, true).tap do |resource_instance|
|
|
25
|
+
resource_instance.prefix_options = prefix_options unless prefix_options.blank?
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/shopify_api/events.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ShopifyAPI
|
|
2
3
|
module Events
|
|
3
4
|
def events
|
|
4
|
-
Event.find(:all, :
|
|
5
|
+
Event.find(:all, params: { resource: self.class.collection_name, resource_id: id })
|
|
5
6
|
end
|
|
6
7
|
end
|
|
7
8
|
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'graphql/client'
|
|
3
|
+
require 'shopify_api/graphql/http_client'
|
|
4
|
+
|
|
5
|
+
module ShopifyAPI
|
|
6
|
+
module GraphQL
|
|
7
|
+
DEFAULT_SCHEMA_LOCATION_PATH = Pathname('shopify_graphql_schemas')
|
|
8
|
+
DEFAULT_EXECUTION_ADAPTER = HTTPClient
|
|
9
|
+
DEFAULT_GRAPHQL_CLIENT = ::GraphQL::Client
|
|
10
|
+
|
|
11
|
+
InvalidSchema = Class.new(StandardError)
|
|
12
|
+
InvalidClient = Class.new(StandardError)
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
delegate :parse, :query, to: :client
|
|
16
|
+
|
|
17
|
+
def client(api_version = ShopifyAPI::Base.api_version.handle)
|
|
18
|
+
initialize_client_cache
|
|
19
|
+
cached_client = @_client_cache[api_version]
|
|
20
|
+
|
|
21
|
+
if cached_client
|
|
22
|
+
cached_client
|
|
23
|
+
else
|
|
24
|
+
schema_file = schema_location.join("#{api_version}.json")
|
|
25
|
+
|
|
26
|
+
if !schema_file.exist?
|
|
27
|
+
raise InvalidClient, <<~MSG
|
|
28
|
+
Client for API version #{api_version} does not exist because no schema file exists at `#{schema_file}`.
|
|
29
|
+
|
|
30
|
+
To dump the schema file, use the `rake shopify_api:graphql:dump` task.
|
|
31
|
+
MSG
|
|
32
|
+
else
|
|
33
|
+
puts(
|
|
34
|
+
'[WARNING] Client was not pre-initialized. Ensure `ShopifyAPI::GraphQL.initialize_clients` ' \
|
|
35
|
+
'is called during app initialization.'
|
|
36
|
+
)
|
|
37
|
+
initialize_clients
|
|
38
|
+
@_client_cache[api_version]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def clear_clients
|
|
44
|
+
@_client_cache = {}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def initialize_clients(raise_on_invalid_schema: true)
|
|
48
|
+
initialize_client_cache
|
|
49
|
+
|
|
50
|
+
Dir.glob(schema_location.join("*.json")).each do |schema_file|
|
|
51
|
+
schema_file = Pathname(schema_file)
|
|
52
|
+
matches = schema_file.basename.to_s.match(/^#{ShopifyAPI::ApiVersion::HANDLE_FORMAT}\.json$/)
|
|
53
|
+
|
|
54
|
+
if matches
|
|
55
|
+
api_version = ShopifyAPI::ApiVersion.new(handle: matches[1])
|
|
56
|
+
elsif raise_on_invalid_schema
|
|
57
|
+
raise InvalidSchema,
|
|
58
|
+
"Invalid schema file name `#{schema_file}`. Does not match format of: `<version>.json`."
|
|
59
|
+
else
|
|
60
|
+
next
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
schema = graphql_client.load_schema(schema_file.to_s)
|
|
64
|
+
client = graphql_client.new(schema: schema, execute: execution_adapter.new(api_version)).tap do |c|
|
|
65
|
+
c.allow_dynamic_queries = true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
@_client_cache[api_version.handle] = client
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def schema_location
|
|
73
|
+
@schema_location || DEFAULT_SCHEMA_LOCATION_PATH
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def schema_location=(path)
|
|
77
|
+
@schema_location = Pathname(path)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def execution_adapter
|
|
81
|
+
@execution_adapter || DEFAULT_EXECUTION_ADAPTER
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def execution_adapter=(executor)
|
|
85
|
+
@execution_adapter = executor
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def graphql_client
|
|
89
|
+
@graphql_client || DEFAULT_GRAPHQL_CLIENT
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def graphql_client=(client)
|
|
93
|
+
@graphql_client = client
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def initialize_client_cache
|
|
99
|
+
@_client_cache ||= {}
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'graphql/client/http'
|
|
3
|
+
|
|
4
|
+
module ShopifyAPI
|
|
5
|
+
module GraphQL
|
|
6
|
+
class HTTPClient < ::GraphQL::Client::HTTP
|
|
7
|
+
def initialize(api_version)
|
|
8
|
+
@api_version = api_version
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def headers(_context)
|
|
12
|
+
ShopifyAPI::Base.headers
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def uri
|
|
16
|
+
ShopifyAPI::Base.site.dup.tap do |uri|
|
|
17
|
+
uri.path = @api_version.construct_graphql_path
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'rails/railtie'
|
|
3
|
+
|
|
4
|
+
module ShopifyAPI
|
|
5
|
+
module GraphQL
|
|
6
|
+
class Railtie < Rails::Railtie
|
|
7
|
+
initializer 'shopify_api.initialize_graphql_clients' do |app|
|
|
8
|
+
ShopifyAPI::GraphQL.schema_location = app.root.join('db', ShopifyAPI::GraphQL.schema_location)
|
|
9
|
+
ShopifyAPI::GraphQL.initialize_clients
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
rake_tasks do
|
|
13
|
+
load 'shopify_api/graphql/task.rake'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
namespace :shopify_api do
|
|
5
|
+
namespace :graphql do
|
|
6
|
+
desc 'Dumps a local JSON schema file of the Shopify Admin API'
|
|
7
|
+
task :dump do
|
|
8
|
+
usage = <<~USAGE
|
|
9
|
+
|
|
10
|
+
Usage: rake shopify_api:graphql:dump [<args>]
|
|
11
|
+
|
|
12
|
+
Dumps a local JSON schema file of the Shopify Admin API. The schema is specific to an
|
|
13
|
+
API version and authentication is required (either OAuth or private app).
|
|
14
|
+
|
|
15
|
+
Dump the schema file for the 2020-01 API version using private app authentication:
|
|
16
|
+
$ rake shopify_api:graphql:dump SHOP_URL="https://API_KEY:PASSWORD@SHOP_NAME.myshopify.com" API_VERSION=2020-01
|
|
17
|
+
|
|
18
|
+
Dump the schema file for the unstable API version using an OAuth access token:
|
|
19
|
+
$ rake shopify_api:graphql:dump SHOP_DOMAIN=SHOP_NAME.myshopify.com ACCESS_TOKEN=abc API_VERSION=unstable
|
|
20
|
+
|
|
21
|
+
See https://github.com/Shopify/shopify_api#getting-started for more
|
|
22
|
+
details on getting started with authenticated API calls.
|
|
23
|
+
|
|
24
|
+
Arguments:
|
|
25
|
+
ACCESS_TOKEN OAuth access token (shop specific)
|
|
26
|
+
API_VERSION API version handle [example: 2020-01]
|
|
27
|
+
SHOP_DOMAIN Shop domain (without path) [example: SHOP_NAME.myshopify.com]
|
|
28
|
+
SHOP_URL Shop URL for private apps [example: https://API_KEY:PASSWORD@SHOP_NAME.myshopify.com]
|
|
29
|
+
USAGE
|
|
30
|
+
|
|
31
|
+
access_token = ENV['ACCESS_TOKEN'] || ENV['access_token']
|
|
32
|
+
api_version = ENV['API_VERSION'] || ENV['api_version']
|
|
33
|
+
shop_url = ENV['SHOP_URL'] || ENV['shop_url']
|
|
34
|
+
shop_domain = ENV['SHOP_DOMAIN'] || ENV['shop_domain']
|
|
35
|
+
|
|
36
|
+
unless access_token || api_version || shop_url || shop_domain
|
|
37
|
+
puts usage
|
|
38
|
+
exit(1)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
unless shop_url || shop_domain
|
|
42
|
+
puts 'Error: either SHOP_DOMAIN or SHOP_URL is required for authentication'
|
|
43
|
+
puts usage
|
|
44
|
+
exit(1)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if shop_url && shop_domain
|
|
48
|
+
puts 'Error: SHOP_DOMAIN and SHOP_URL cannot be used together. Use one or the other for authentication.'
|
|
49
|
+
puts usage
|
|
50
|
+
exit(1)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
if shop_domain && !access_token
|
|
54
|
+
puts 'Error: ACCESS_TOKEN required when SHOP_DOMAIN is used'
|
|
55
|
+
puts usage
|
|
56
|
+
exit(1)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
unless api_version
|
|
60
|
+
puts 'Error: API_VERSION required. Example: 2020-01'
|
|
61
|
+
puts usage
|
|
62
|
+
exit(1)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
Rake::Task['environment'].invoke if Rake::Task.task_defined?('environment')
|
|
66
|
+
|
|
67
|
+
ShopifyAPI::ApiVersion.fetch_known_versions
|
|
68
|
+
ShopifyAPI::ApiVersion.version_lookup_mode = :raise_on_unknown
|
|
69
|
+
|
|
70
|
+
shopify_session = ShopifyAPI::Session.new(domain: shop_domain, token: access_token, api_version: api_version)
|
|
71
|
+
ShopifyAPI::Base.activate_session(shopify_session)
|
|
72
|
+
|
|
73
|
+
if shop_url
|
|
74
|
+
ShopifyAPI::Base.site = shop_url
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
puts "Fetching schema for #{ShopifyAPI::Base.api_version.handle} API version..."
|
|
78
|
+
|
|
79
|
+
client = ShopifyAPI::GraphQL::HTTPClient.new(ShopifyAPI::Base.api_version)
|
|
80
|
+
document = GraphQL.parse('{ __schema { queryType { name } } }')
|
|
81
|
+
response = client.execute(document: document).to_h
|
|
82
|
+
|
|
83
|
+
unless response['data'].present?
|
|
84
|
+
puts "Error: failed to query the API."
|
|
85
|
+
puts "Response: #{response}"
|
|
86
|
+
puts 'Ensure your SHOP_DOMAIN or SHOP_URL are valid and you have valid authentication credentials.'
|
|
87
|
+
puts usage
|
|
88
|
+
exit(1)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
schema_location = ShopifyAPI::GraphQL.schema_location
|
|
92
|
+
FileUtils.mkdir_p(schema_location) unless Dir.exist?(schema_location)
|
|
93
|
+
|
|
94
|
+
schema_file = schema_location.join("#{api_version}.json")
|
|
95
|
+
GraphQL::Client.dump_schema(client, schema_file.to_s)
|
|
96
|
+
|
|
97
|
+
puts "Wrote file #{schema_file}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
data/lib/shopify_api/limits.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ShopifyAPI
|
|
2
3
|
module Limits
|
|
3
4
|
class LimitUnavailable < StandardError; end
|
|
@@ -7,14 +8,14 @@ module ShopifyAPI
|
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
module ClassMethods
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
# Eg:
|
|
11
|
+
# Takes form <call count>/<bucket size>
|
|
12
|
+
# See https://help.shopify.com/en/api/getting-started/api-call-limit
|
|
13
|
+
# Eg: 2/40
|
|
13
14
|
CREDIT_LIMIT_HEADER_PARAM = {
|
|
14
|
-
:
|
|
15
|
+
shop: 'X-Shopify-Shop-Api-Call-Limit',
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
##
|
|
18
19
|
# How many more API calls can I make?
|
|
19
20
|
# @return {Integer}
|
|
20
21
|
#
|
|
@@ -38,9 +39,8 @@ module ShopifyAPI
|
|
|
38
39
|
# @param {Symbol} scope [:shop]
|
|
39
40
|
# @return {Integer}
|
|
40
41
|
#
|
|
41
|
-
def credit_limit(scope
|
|
42
|
-
|
|
43
|
-
@api_credit_limit[scope] ||= api_credit_limit_param(scope).pop.to_i - 1
|
|
42
|
+
def credit_limit(scope = :shop)
|
|
43
|
+
api_credit_limit_param(scope).pop.to_i - 1
|
|
44
44
|
end
|
|
45
45
|
alias_method :call_limit, :credit_limit
|
|
46
46
|
|
|
@@ -49,7 +49,7 @@ module ShopifyAPI
|
|
|
49
49
|
# @param {Symbol} scope [:shop]
|
|
50
50
|
# @return {Integer}
|
|
51
51
|
#
|
|
52
|
-
def credit_used(scope
|
|
52
|
+
def credit_used(scope = :shop)
|
|
53
53
|
api_credit_limit_param(scope).shift.to_i
|
|
54
54
|
end
|
|
55
55
|
alias_method :call_count, :credit_used
|
|
@@ -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
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module ShopifyAPI
|
|
2
3
|
module Metafields
|
|
3
4
|
def metafields(**options)
|
|
4
|
-
options.merge!
|
|
5
|
+
options.merge!(resource: self.class.collection_name, resource_id: id)
|
|
5
6
|
|
|
6
|
-
Metafield.find
|
|
7
|
+
Metafield.find(:all, params: options)
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
def add_metafield(metafield)
|
|
10
11
|
raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
|
|
11
12
|
|
|
12
13
|
metafield.prefix_options = {
|
|
13
|
-
:
|
|
14
|
-
:
|
|
14
|
+
resource: self.class.collection_name,
|
|
15
|
+
resource_id: id,
|
|
15
16
|
}
|
|
16
17
|
metafield.save
|
|
17
18
|
metafield
|
|
@@ -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
|