duodealer_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.
Files changed (322) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +13 -0
  4. data/.rubocop.yml +8 -0
  5. data/.travis.yml +23 -0
  6. data/Gemfile +5 -0
  7. data/Gemfile_ar41 +5 -0
  8. data/Gemfile_ar50 +5 -0
  9. data/Gemfile_ar51 +5 -0
  10. data/Gemfile_ar_master +5 -0
  11. data/RELEASING +13 -0
  12. data/Rakefile +44 -0
  13. data/docker-compose.yml +13 -0
  14. data/docs/graphql.md +191 -0
  15. data/duodealer_api.gemspec +38 -0
  16. data/lib/active_resource/connection_ext.rb +10 -0
  17. data/lib/active_resource/detailed_log_subscriber.rb +52 -0
  18. data/lib/active_resource/json_errors.rb +31 -0
  19. data/lib/duodealer_api.rb +33 -0
  20. data/lib/duodealer_api/api_version.rb +205 -0
  21. data/lib/duodealer_api/connection.rb +35 -0
  22. data/lib/duodealer_api/countable.rb +14 -0
  23. data/lib/duodealer_api/disable_prefix_check.rb +31 -0
  24. data/lib/duodealer_api/events.rb +7 -0
  25. data/lib/duodealer_api/graphql.rb +79 -0
  26. data/lib/duodealer_api/graphql/http_client.rb +22 -0
  27. data/lib/duodealer_api/graphql/railtie.rb +17 -0
  28. data/lib/duodealer_api/graphql/task.rake +97 -0
  29. data/lib/duodealer_api/limits.rb +76 -0
  30. data/lib/duodealer_api/message_enricher.rb +23 -0
  31. data/lib/duodealer_api/meta.rb +15 -0
  32. data/lib/duodealer_api/metafields.rb +20 -0
  33. data/lib/duodealer_api/paginated_collection.rb +69 -0
  34. data/lib/duodealer_api/pagination_link_headers.rb +33 -0
  35. data/lib/duodealer_api/resources.rb +3 -0
  36. data/lib/duodealer_api/resources/abandoned_checkout.rb +7 -0
  37. data/lib/duodealer_api/resources/access_scope.rb +10 -0
  38. data/lib/duodealer_api/resources/access_token.rb +8 -0
  39. data/lib/duodealer_api/resources/address.rb +4 -0
  40. data/lib/duodealer_api/resources/announcement.rb +4 -0
  41. data/lib/duodealer_api/resources/api_permission.rb +9 -0
  42. data/lib/duodealer_api/resources/application_charge.rb +15 -0
  43. data/lib/duodealer_api/resources/application_credit.rb +4 -0
  44. data/lib/duodealer_api/resources/array_base.rb +13 -0
  45. data/lib/duodealer_api/resources/article.rb +21 -0
  46. data/lib/duodealer_api/resources/asset.rb +100 -0
  47. data/lib/duodealer_api/resources/assigned_fulfillment_order.rb +16 -0
  48. data/lib/duodealer_api/resources/base.rb +162 -0
  49. data/lib/duodealer_api/resources/billing_address.rb +4 -0
  50. data/lib/duodealer_api/resources/blog.rb +10 -0
  51. data/lib/duodealer_api/resources/carrier_service.rb +4 -0
  52. data/lib/duodealer_api/resources/cart.rb +4 -0
  53. data/lib/duodealer_api/resources/checkout.rb +30 -0
  54. data/lib/duodealer_api/resources/collect.rb +6 -0
  55. data/lib/duodealer_api/resources/collection.rb +14 -0
  56. data/lib/duodealer_api/resources/collection_listing.rb +18 -0
  57. data/lib/duodealer_api/resources/collection_publication.rb +10 -0
  58. data/lib/duodealer_api/resources/comment.rb +9 -0
  59. data/lib/duodealer_api/resources/country.rb +4 -0
  60. data/lib/duodealer_api/resources/currency.rb +6 -0
  61. data/lib/duodealer_api/resources/custom_collection.rb +19 -0
  62. data/lib/duodealer_api/resources/customer.rb +29 -0
  63. data/lib/duodealer_api/resources/customer_group.rb +5 -0
  64. data/lib/duodealer_api/resources/customer_invite.rb +4 -0
  65. data/lib/duodealer_api/resources/customer_saved_search.rb +11 -0
  66. data/lib/duodealer_api/resources/discount_code.rb +9 -0
  67. data/lib/duodealer_api/resources/draft_order.rb +14 -0
  68. data/lib/duodealer_api/resources/draft_order_invoice.rb +4 -0
  69. data/lib/duodealer_api/resources/event.rb +8 -0
  70. data/lib/duodealer_api/resources/fulfillment.rb +47 -0
  71. data/lib/duodealer_api/resources/fulfillment_event.rb +15 -0
  72. data/lib/duodealer_api/resources/fulfillment_order.rb +137 -0
  73. data/lib/duodealer_api/resources/fulfillment_order_locations_for_move.rb +4 -0
  74. data/lib/duodealer_api/resources/fulfillment_request.rb +15 -0
  75. data/lib/duodealer_api/resources/fulfillment_service.rb +4 -0
  76. data/lib/duodealer_api/resources/fulfillment_v2.rb +20 -0
  77. data/lib/duodealer_api/resources/gift_card.rb +7 -0
  78. data/lib/duodealer_api/resources/image.rb +16 -0
  79. data/lib/duodealer_api/resources/inventory_item.rb +6 -0
  80. data/lib/duodealer_api/resources/inventory_level.rb +55 -0
  81. data/lib/duodealer_api/resources/line_item.rb +14 -0
  82. data/lib/duodealer_api/resources/location.rb +8 -0
  83. data/lib/duodealer_api/resources/marketing_event.rb +10 -0
  84. data/lib/duodealer_api/resources/metafield.rb +13 -0
  85. data/lib/duodealer_api/resources/note_attribute.rb +4 -0
  86. data/lib/duodealer_api/resources/option.rb +4 -0
  87. data/lib/duodealer_api/resources/order.rb +43 -0
  88. data/lib/duodealer_api/resources/order_risk.rb +8 -0
  89. data/lib/duodealer_api/resources/page.rb +6 -0
  90. data/lib/duodealer_api/resources/payment.rb +7 -0
  91. data/lib/duodealer_api/resources/payment_details.rb +4 -0
  92. data/lib/duodealer_api/resources/ping.rb +3 -0
  93. data/lib/duodealer_api/resources/policy.rb +7 -0
  94. data/lib/duodealer_api/resources/price_rule.rb +8 -0
  95. data/lib/duodealer_api/resources/product.rb +35 -0
  96. data/lib/duodealer_api/resources/product_listing.rb +16 -0
  97. data/lib/duodealer_api/resources/product_publication.rb +10 -0
  98. data/lib/duodealer_api/resources/province.rb +5 -0
  99. data/lib/duodealer_api/resources/publication.rb +5 -0
  100. data/lib/duodealer_api/resources/receipt.rb +4 -0
  101. data/lib/duodealer_api/resources/recurring_application_charge.rb +31 -0
  102. data/lib/duodealer_api/resources/redirect.rb +4 -0
  103. data/lib/duodealer_api/resources/refund.rb +14 -0
  104. data/lib/duodealer_api/resources/report.rb +4 -0
  105. data/lib/duodealer_api/resources/resource_feedback.rb +19 -0
  106. data/lib/duodealer_api/resources/rule.rb +4 -0
  107. data/lib/duodealer_api/resources/script_tag.rb +4 -0
  108. data/lib/duodealer_api/resources/shipping_address.rb +4 -0
  109. data/lib/duodealer_api/resources/shipping_line.rb +4 -0
  110. data/lib/duodealer_api/resources/shipping_rate.rb +7 -0
  111. data/lib/duodealer_api/resources/shipping_zone.rb +4 -0
  112. data/lib/duodealer_api/resources/shop.rb +25 -0
  113. data/lib/duodealer_api/resources/smart_collection.rb +19 -0
  114. data/lib/duodealer_api/resources/storefront_access_token.rb +4 -0
  115. data/lib/duodealer_api/resources/tax_line.rb +4 -0
  116. data/lib/duodealer_api/resources/tax_service.rb +4 -0
  117. data/lib/duodealer_api/resources/tender_transaction.rb +6 -0
  118. data/lib/duodealer_api/resources/theme.rb +4 -0
  119. data/lib/duodealer_api/resources/transaction.rb +5 -0
  120. data/lib/duodealer_api/resources/usage_charge.rb +5 -0
  121. data/lib/duodealer_api/resources/user.rb +4 -0
  122. data/lib/duodealer_api/resources/variant.rb +8 -0
  123. data/lib/duodealer_api/resources/webhook.rb +4 -0
  124. data/lib/duodealer_api/session.rb +171 -0
  125. data/lib/duodealer_api/version.rb +3 -0
  126. data/shipit.rubygems.yml +1 -0
  127. data/test/abandoned_checkouts_test.rb +29 -0
  128. data/test/access_scope_test.rb +23 -0
  129. data/test/access_token_test.rb +19 -0
  130. data/test/active_resource/json_errors_test.rb +19 -0
  131. data/test/api_permission_test.rb +9 -0
  132. data/test/api_version_test.rb +157 -0
  133. data/test/application_charge_test.rb +79 -0
  134. data/test/application_credit_test.rb +35 -0
  135. data/test/article_test.rb +73 -0
  136. data/test/asset_test.rb +18 -0
  137. data/test/assigned_fulfillment_order_test.rb +77 -0
  138. data/test/base_test.rb +198 -0
  139. data/test/blog_test.rb +8 -0
  140. data/test/carrier_service_test.rb +17 -0
  141. data/test/cart_test.rb +13 -0
  142. data/test/checkouts_test.rb +77 -0
  143. data/test/collect_test.rb +9 -0
  144. data/test/collection_listing_test.rb +79 -0
  145. data/test/collection_publication_test.rb +40 -0
  146. data/test/collection_test.rb +49 -0
  147. data/test/countable_test.rb +13 -0
  148. data/test/currency_test.rb +21 -0
  149. data/test/custom_collection_test.rb +9 -0
  150. data/test/customer_saved_search_test.rb +27 -0
  151. data/test/customer_test.rb +50 -0
  152. data/test/detailed_log_subscriber_test.rb +139 -0
  153. data/test/discount_code_test.rb +53 -0
  154. data/test/draft_order_test.rb +151 -0
  155. data/test/fixtures/abandoned_checkout.json +184 -0
  156. data/test/fixtures/abandoned_checkouts.json +186 -0
  157. data/test/fixtures/access_scopes.json +10 -0
  158. data/test/fixtures/access_token_delegate.json +4 -0
  159. data/test/fixtures/api_versions.json +38 -0
  160. data/test/fixtures/apis.json +42 -0
  161. data/test/fixtures/application_charge.json +16 -0
  162. data/test/fixtures/application_charges.json +57 -0
  163. data/test/fixtures/application_credit.json +12 -0
  164. data/test/fixtures/application_credits.json +24 -0
  165. data/test/fixtures/article.json +15 -0
  166. data/test/fixtures/articles.json +39 -0
  167. data/test/fixtures/asset.json +9 -0
  168. data/test/fixtures/assets.json +136 -0
  169. data/test/fixtures/assigned_fulfillment_orders.json +78 -0
  170. data/test/fixtures/authors.json +1 -0
  171. data/test/fixtures/blog.json +13 -0
  172. data/test/fixtures/blogs.json +13 -0
  173. data/test/fixtures/carrier_service.json +9 -0
  174. data/test/fixtures/carts.json +43 -0
  175. data/test/fixtures/checkout.json +160 -0
  176. data/test/fixtures/checkouts.json +162 -0
  177. data/test/fixtures/collect.json +12 -0
  178. data/test/fixtures/collection.json +17 -0
  179. data/test/fixtures/collection_listing.json +11 -0
  180. data/test/fixtures/collection_listing_product_ids.json +1 -0
  181. data/test/fixtures/collection_listing_product_ids2.json +1 -0
  182. data/test/fixtures/collection_listings.json +13 -0
  183. data/test/fixtures/collection_products.json +47 -0
  184. data/test/fixtures/collection_publication.json +11 -0
  185. data/test/fixtures/collection_publications.json +13 -0
  186. data/test/fixtures/currencies.json +25 -0
  187. data/test/fixtures/custom_collection.json +17 -0
  188. data/test/fixtures/customer_invite.json +9 -0
  189. data/test/fixtures/customer_saved_search.json +9 -0
  190. data/test/fixtures/customer_saved_search_customers.json +60 -0
  191. data/test/fixtures/customers.json +59 -0
  192. data/test/fixtures/customers_account_activation_url.json +3 -0
  193. data/test/fixtures/customers_search.json +60 -0
  194. data/test/fixtures/discount_code.json +10 -0
  195. data/test/fixtures/discount_codes.json +12 -0
  196. data/test/fixtures/draft_order.json +159 -0
  197. data/test/fixtures/draft_order_completed.json +159 -0
  198. data/test/fixtures/draft_order_invoice.json +9 -0
  199. data/test/fixtures/draft_orders.json +161 -0
  200. data/test/fixtures/engagement.json +15 -0
  201. data/test/fixtures/events.json +31 -0
  202. data/test/fixtures/fulfillment.json +49 -0
  203. data/test/fixtures/fulfillment_event.json +12 -0
  204. data/test/fixtures/fulfillment_order.json +38 -0
  205. data/test/fixtures/fulfillment_order_locations_for_move.json +18 -0
  206. data/test/fixtures/fulfillment_orders.json +78 -0
  207. data/test/fixtures/fulfillment_request.json +28 -0
  208. data/test/fixtures/fulfillment_service.json +10 -0
  209. data/test/fixtures/fulfillments.json +53 -0
  210. data/test/fixtures/gift_card.json +20 -0
  211. data/test/fixtures/gift_card_disabled.json +20 -0
  212. data/test/fixtures/graphql/2019-10.json +1083 -0
  213. data/test/fixtures/graphql/dummy_schema.rb +16 -0
  214. data/test/fixtures/graphql/unstable.json +1083 -0
  215. data/test/fixtures/image.json +10 -0
  216. data/test/fixtures/images.json +20 -0
  217. data/test/fixtures/inventory_level.json +7 -0
  218. data/test/fixtures/inventory_levels.json +24 -0
  219. data/test/fixtures/marketing_event.json +28 -0
  220. data/test/fixtures/marketing_events.json +54 -0
  221. data/test/fixtures/metafield.json +12 -0
  222. data/test/fixtures/metafields.json +34 -0
  223. data/test/fixtures/order.json +297 -0
  224. data/test/fixtures/order_risk.json +14 -0
  225. data/test/fixtures/order_risks.json +28 -0
  226. data/test/fixtures/order_with_properties.json +373 -0
  227. data/test/fixtures/orders.json +299 -0
  228. data/test/fixtures/payment.json +7 -0
  229. data/test/fixtures/payments.json +9 -0
  230. data/test/fixtures/ping/conversation.json +1 -0
  231. data/test/fixtures/ping/failed_delivery_confirmation.json +1 -0
  232. data/test/fixtures/ping/message.json +1 -0
  233. data/test/fixtures/ping/successful_delivery_confirmation.json +1 -0
  234. data/test/fixtures/policies.json +8 -0
  235. data/test/fixtures/price_rule.json +27 -0
  236. data/test/fixtures/price_rules.json +28 -0
  237. data/test/fixtures/product.json +116 -0
  238. data/test/fixtures/product_listing.json +86 -0
  239. data/test/fixtures/product_listing_product_ids.json +1 -0
  240. data/test/fixtures/product_listing_product_ids2.json +1 -0
  241. data/test/fixtures/product_listings.json +174 -0
  242. data/test/fixtures/product_publication.json +11 -0
  243. data/test/fixtures/product_publications.json +13 -0
  244. data/test/fixtures/publications.json +9 -0
  245. data/test/fixtures/recurring_application_charge.json +22 -0
  246. data/test/fixtures/recurring_application_charge_adjustment.json +5 -0
  247. data/test/fixtures/recurring_application_charges.json +106 -0
  248. data/test/fixtures/redirect.json +7 -0
  249. data/test/fixtures/refund.json +112 -0
  250. data/test/fixtures/report.json +9 -0
  251. data/test/fixtures/reports.json +11 -0
  252. data/test/fixtures/script_tag.json +10 -0
  253. data/test/fixtures/script_tags.json +18 -0
  254. data/test/fixtures/shipping_rates.json +12 -0
  255. data/test/fixtures/shipping_zones.json +315 -0
  256. data/test/fixtures/shop.json +26 -0
  257. data/test/fixtures/smart_collection.json +21 -0
  258. data/test/fixtures/smart_collection_products.json +155 -0
  259. data/test/fixtures/storefront_access_token.json +9 -0
  260. data/test/fixtures/storefront_access_tokens.json +18 -0
  261. data/test/fixtures/tags.json +1 -0
  262. data/test/fixtures/tax_service.json +9 -0
  263. data/test/fixtures/tender_transactions.json +52 -0
  264. data/test/fixtures/transaction.json +29 -0
  265. data/test/fixtures/usage_charge.json +11 -0
  266. data/test/fixtures/usage_charges.json +23 -0
  267. data/test/fixtures/user.json +21 -0
  268. data/test/fixtures/users.json +42 -0
  269. data/test/fixtures/variant.json +23 -0
  270. data/test/fixtures/variants.json +88 -0
  271. data/test/fixtures/webhook.json +10 -0
  272. data/test/fixtures/webhooks.json +18 -0
  273. data/test/fulfillment_event_test.rb +69 -0
  274. data/test/fulfillment_order_test.rb +462 -0
  275. data/test/fulfillment_order_test_helper.rb +7 -0
  276. data/test/fulfillment_request_test.rb +33 -0
  277. data/test/fulfillment_service_test.rb +17 -0
  278. data/test/fulfillment_test.rb +224 -0
  279. data/test/fulfillment_v2_test.rb +62 -0
  280. data/test/gift_card_test.rb +22 -0
  281. data/test/graphql/http_client_test.rb +26 -0
  282. data/test/graphql_test.rb +147 -0
  283. data/test/image_test.rb +39 -0
  284. data/test/inventory_level_test.rb +59 -0
  285. data/test/lib/webmock_extensions/last_request.rb +16 -0
  286. data/test/limits_test.rb +38 -0
  287. data/test/location_test.rb +14 -0
  288. data/test/marketing_event_test.rb +68 -0
  289. data/test/message_enricher_test.rb +45 -0
  290. data/test/meta_test.rb +49 -0
  291. data/test/metafield_test.rb +46 -0
  292. data/test/order_risk_test.rb +46 -0
  293. data/test/order_test.rb +125 -0
  294. data/test/pagination_test.rb +257 -0
  295. data/test/payment_test.rb +19 -0
  296. data/test/policy_test.rb +19 -0
  297. data/test/price_rule_test.rb +65 -0
  298. data/test/product_listing_test.rb +97 -0
  299. data/test/product_publication_test.rb +40 -0
  300. data/test/product_test.rb +60 -0
  301. data/test/publication_test.rb +12 -0
  302. data/test/recurring_application_charge_test.rb +142 -0
  303. data/test/redirect_test.rb +9 -0
  304. data/test/refund_test.rb +32 -0
  305. data/test/report_test.rb +35 -0
  306. data/test/resource_feedback_test.rb +42 -0
  307. data/test/script_tag_test.rb +30 -0
  308. data/test/session_test.rb +366 -0
  309. data/test/shipping_rate_test.rb +17 -0
  310. data/test/shipping_zone_test.rb +10 -0
  311. data/test/shop_test.rb +68 -0
  312. data/test/smart_collection_test.rb +35 -0
  313. data/test/storefront_access_token_test.rb +32 -0
  314. data/test/tax_service_test.rb +9 -0
  315. data/test/tender_transaction_test.rb +18 -0
  316. data/test/test_helper.rb +124 -0
  317. data/test/transaction_test.rb +17 -0
  318. data/test/usage_charge_test.rb +21 -0
  319. data/test/user_test.rb +17 -0
  320. data/test/variant_test.rb +46 -0
  321. data/test/webhook_test.rb +21 -0
  322. metadata +541 -0
@@ -0,0 +1,4 @@
1
+ module DuodealerAPI
2
+ class User < Base
3
+ end
4
+ end
@@ -0,0 +1,8 @@
1
+ module DuodealerAPI
2
+ class Variant < Base
3
+ include Metafields
4
+ include DisablePrefixCheck
5
+
6
+ conditional_prefix :product
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ module DuodealerAPI
2
+ class Webhook < Base
3
+ end
4
+ end
@@ -0,0 +1,171 @@
1
+ require 'openssl'
2
+ require 'rack'
3
+
4
+ module DuodealerAPI
5
+ class ValidationException < StandardError
6
+ end
7
+
8
+ class Session
9
+ cattr_accessor :api_key, :secret, :duodealer_domain
10
+ self.duodealer_domain = 'duodealer.com'
11
+
12
+ attr_accessor :domain, :token, :name, :extra
13
+ attr_reader :api_version
14
+ alias_method :url, :domain
15
+
16
+ class << self
17
+
18
+ def setup(params)
19
+ params.each { |k,value| public_send("#{k}=", value) }
20
+ end
21
+
22
+ def temp(domain:, token:, api_version:, &block)
23
+ session = new(domain: domain, token: token, api_version: api_version)
24
+
25
+ with_session(session, &block)
26
+ end
27
+
28
+ def with_session(session, &_block)
29
+ original_session = extract_current_session
30
+
31
+ begin
32
+ DuodealerAPI::Base.activate_session(session)
33
+ yield
34
+ ensure
35
+ DuodealerAPI::Base.activate_session(original_session)
36
+ end
37
+ end
38
+
39
+ def with_version(api_version, &block)
40
+ original_session = extract_current_session
41
+ session = new(domain: original_session.site, token: original_session.token, api_version: api_version)
42
+
43
+ with_session(session, &block)
44
+ end
45
+
46
+ def prepare_domain(domain)
47
+ return nil if domain.blank?
48
+ # remove http:// or https://
49
+ domain = domain.strip.gsub(%r{\Ahttps?://}, '')
50
+ # extract host, removing any username, password or path
51
+ shop = URI.parse("https://#{domain}").host
52
+ # extract subdomain of .duodealer.com
53
+ if idx = shop.index(".")
54
+ shop = shop.slice(0, idx)
55
+ end
56
+ return nil if shop.empty?
57
+ "#{shop}.#{duodealer_domain}"
58
+ rescue URI::InvalidURIError
59
+ nil
60
+ end
61
+
62
+ def validate_signature(params)
63
+ params = (params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params).with_indifferent_access
64
+ return false unless signature = params[:hmac]
65
+
66
+ calculated_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new(), secret, encoded_params_for_signature(params))
67
+
68
+ Rack::Utils.secure_compare(calculated_signature, signature)
69
+ end
70
+
71
+ private
72
+
73
+ def encoded_params_for_signature(params)
74
+ params = params.except(:signature, :hmac, :action, :controller)
75
+ params.map{|k,v| "#{URI.escape(k.to_s, '&=%')}=#{URI.escape(v.to_s, '&%')}"}.sort.join('&')
76
+ end
77
+
78
+ def extract_current_session
79
+ site = DuodealerAPI::Base.site.to_s
80
+ token = DuodealerAPI::Base.headers['X-DuoDealer-Access-Token']
81
+ version = DuodealerAPI::Base.api_version
82
+ new(domain: site, token: token, api_version: version)
83
+ end
84
+ end
85
+
86
+ def initialize(domain:, token:, api_version:, extra: {})
87
+ self.domain = self.class.prepare_domain(domain)
88
+ self.api_version = api_version
89
+ self.token = token
90
+ self.extra = extra
91
+ end
92
+
93
+ def create_permission_url(scope, redirect_uri, options = {})
94
+ params = { client_id: api_key, scope: scope.join(','), redirect_uri: redirect_uri }
95
+ params[:state] = options[:state] if options[:state]
96
+ construct_oauth_url("authorize", params)
97
+ end
98
+
99
+ def request_token(params)
100
+ return token if token
101
+
102
+ unless self.class.validate_signature(params) && params[:timestamp].to_i > 24.hours.ago.utc.to_i
103
+ raise DuodealerAPI::ValidationException, "Invalid Signature: Possible malicious login"
104
+ end
105
+
106
+ response = access_token_request(params[:code])
107
+ if response.code == "200"
108
+ self.extra = JSON.parse(response.body)
109
+ self.token = extra.delete('access_token')
110
+
111
+ if expires_in = extra.delete('expires_in')
112
+ extra['expires_at'] = Time.now.utc.to_i + expires_in
113
+ end
114
+ token
115
+ else
116
+ raise RuntimeError, response.msg
117
+ end
118
+ end
119
+
120
+ def shop
121
+ Shop.current
122
+ end
123
+
124
+ def site
125
+ "https://#{domain}"
126
+ end
127
+
128
+ def api_version=(version)
129
+ @api_version = ApiVersion::NullVersion.matches?(version) ? ApiVersion::NullVersion : ApiVersion.find_version(version)
130
+ end
131
+
132
+ def valid?
133
+ domain.present? && token.present? && api_version.is_a?(ApiVersion)
134
+ end
135
+
136
+ def expires_in
137
+ return unless expires_at.present?
138
+ [0, expires_at.to_i - Time.now.utc.to_i].max
139
+ end
140
+
141
+ def expires_at
142
+ return unless extra.present?
143
+ @expires_at ||= Time.at(extra['expires_at']).utc
144
+ end
145
+
146
+ def expired?
147
+ return false if expires_in.nil?
148
+ expires_in <= 0
149
+ end
150
+
151
+ private
152
+
153
+ def parameterize(params)
154
+ URI.escape(params.collect { |k, v| "#{k}=#{v}" }.join('&'))
155
+ end
156
+
157
+ def access_token_request(code)
158
+ uri = URI.parse(construct_oauth_url('access_token'))
159
+ https = Net::HTTP.new(uri.host, uri.port)
160
+ https.use_ssl = true
161
+ request = Net::HTTP::Post.new(uri.request_uri)
162
+ request.set_form_data('client_id' => api_key, 'client_secret' => secret, 'code' => code)
163
+ https.request(request)
164
+ end
165
+
166
+ def construct_oauth_url(path, query_params = {})
167
+ query_string = "?#{parameterize(query_params)}" unless query_params.empty?
168
+ "https://#{domain}/admin/oauth/#{path}#{query_string}"
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,3 @@
1
+ module DuodealerAPI
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1 @@
1
+ # using the default shipit config
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ class AbandonedCheckoutsTest < Test::Unit::TestCase
5
+ def setup
6
+ super
7
+
8
+ @expected_checkouts = JSON.parse(load_fixture('abandoned_checkouts'))['checkouts']
9
+ @expected_checkout_id = JSON.parse(load_fixture('abandoned_checkout'))['checkout']['id']
10
+ end
11
+
12
+ test ":create creates a checkout" do
13
+ fake 'checkouts', method: :post, status: 201, body: load_fixture('abandoned_checkout')
14
+
15
+ checkout = DuodealerAPI::AbandonedCheckout.create
16
+
17
+ assert_equal @expected_checkout_id, checkout.id
18
+ assert_equal true, checkout.attributes.include?(:abandoned_checkout_url)
19
+ end
20
+
21
+ test "get all checkouts indexed by token" do
22
+ fake 'checkouts', method: :get, status: 200, body: load_fixture('abandoned_checkouts')
23
+
24
+ checkouts = DuodealerAPI::AbandonedCheckout.all
25
+
26
+ assert_equal @expected_checkout_id, checkouts.first.id
27
+ assert_equal @expected_checkouts.size, checkouts.size
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ class AccessScopeTest < Test::Unit::TestCase
5
+ test 'access scope does not use the versioned resource urls' do
6
+ fake(
7
+ 'access_scopes',
8
+ url: 'https://shop2.duodealer.com/admin/oauth/access_scopes.json',
9
+ method: :get,
10
+ status: 201,
11
+ body: load_fixture('access_scopes'),
12
+ extension: false
13
+ )
14
+
15
+ unstable_version = DuodealerAPI::Session.new(domain: 'shop2.duodealer.com', token: 'token2', api_version: :unstable)
16
+
17
+ DuodealerAPI::Base.activate_session(unstable_version)
18
+
19
+ scope_handles = DuodealerAPI::AccessScope.find(:all).map(&:handle)
20
+
21
+ assert_equal(['write_product_listings', 'read_shipping'], scope_handles)
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+
3
+ class AccessTokenTest < Test::Unit::TestCase
4
+
5
+ def test_delegate_access_token
6
+ fake "access_tokens/delegate.json?delegate_access_scope%5B%5D=write_orders&" \
7
+ "delegate_access_scope%5B%5D=read_products&expires_in=",
8
+ method: :post,
9
+ status: 201,
10
+ body: load_fixture('access_token_delegate'),
11
+ extension: false
12
+
13
+ delegate_scope = ['write_orders', 'read_products']
14
+ token = DuodealerAPI::AccessToken.delegate(delegate_scope)
15
+
16
+ assert_equal 'abracadabra', token.access_token
17
+ assert_equal 'write_orders,read_products', token.scope
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveResource
4
+ class JsonErrorsTest < Test::Unit::TestCase
5
+
6
+ def test_parsing_of_error_json_hash
7
+ @model = DuodealerAPI::Order.new
8
+ @model.errors.from_json({errors: {name: ['missing']}}.to_json)
9
+ assert_equal ['missing'], @model.errors[:name]
10
+ end
11
+
12
+ def test_parsing_of_error_json_plain_string
13
+ @model = DuodealerAPI::Order.new
14
+ @model.errors.from_json({errors: 'some generic error'}.to_json)
15
+ assert_equal ['some generic error'], @model.errors[:base]
16
+ assert_equal 'some generic error', @model.errors.full_messages.to_sentence
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ class ApiPermissionTest < Test::Unit::TestCase
5
+ test "revoke access token" do
6
+ fake "api_permissions/current", method: :delete, status: 200, body: "{}"
7
+ assert DuodealerAPI::ApiPermission.destroy
8
+ end
9
+ end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ class ApiVersionTest < Test::Unit::TestCase
5
+ test "find_version returns any version object given" do
6
+ version = DuodealerAPI::ApiVersion.new(handle: :unstable)
7
+ assert_same(version, DuodealerAPI::ApiVersion.find_version(version))
8
+ end
9
+
10
+ test "find_version converts a known version into a version object" do
11
+ versions = [
12
+ DuodealerAPI::ApiVersion.new(handle: :unstable),
13
+ DuodealerAPI::ApiVersion.new(handle: '2019-01'),
14
+ ]
15
+
16
+ assert_equal(versions, [
17
+ DuodealerAPI::ApiVersion.find_version('unstable'),
18
+ DuodealerAPI::ApiVersion.find_version('2019-01'),
19
+ ])
20
+ end
21
+
22
+ test "find_version removes unverified versions from version set if mode is set to :raise_on_unknown" do
23
+ DuodealerAPI::ApiVersion.version_lookup_mode = :define_on_unknown
24
+ assert DuodealerAPI::ApiVersion.versions.values.all?(&:verified?)
25
+ assert_equal 6, DuodealerAPI::ApiVersion.versions.size
26
+
27
+ DuodealerAPI::ApiVersion.find_version('2019-30')
28
+ refute DuodealerAPI::ApiVersion.versions.values.all?(&:verified?)
29
+ assert_equal 7, DuodealerAPI::ApiVersion.versions.size
30
+ DuodealerAPI::ApiVersion.version_lookup_mode = :raise_on_unknown
31
+
32
+ assert DuodealerAPI::ApiVersion.versions.values.all?(&:verified?)
33
+ assert_equal 6, DuodealerAPI::ApiVersion.versions.size
34
+ end
35
+
36
+ test "find_version does not raise when coercing a string if no versions are defined when version_lookup_mode is :define_on_unknown" do
37
+ DuodealerAPI::ApiVersion.clear_known_versions
38
+ DuodealerAPI::ApiVersion.version_lookup_mode = :define_on_unknown
39
+ assert_equal :define_on_unknown, DuodealerAPI::ApiVersion.version_lookup_mode
40
+ assert_nothing_raised do
41
+ DuodealerAPI::ApiVersion.find_version('made up version')
42
+ end
43
+ end
44
+
45
+ test "find_version does raise when coercing a string if no versions are defined when version_lookup_mode is :raise_on_unknown" do
46
+ refute DuodealerAPI::ApiVersion.versions['made up version']
47
+ DuodealerAPI::ApiVersion.version_lookup_mode = :raise_on_unknown
48
+ assert_raises DuodealerAPI::ApiVersion::UnknownVersion do
49
+ DuodealerAPI::ApiVersion.find_version('made up version')
50
+ end
51
+ end
52
+
53
+ test "find_version raises ArgumentError when given an DuodealerAPI::ApiVersion::NullVersion object" do
54
+ DuodealerAPI::ApiVersion.clear_known_versions
55
+ DuodealerAPI::ApiVersion.version_lookup_mode = :define_on_unknown
56
+ assert_equal :define_on_unknown, DuodealerAPI::ApiVersion.version_lookup_mode
57
+ assert_raises ArgumentError do
58
+ DuodealerAPI::ApiVersion.find_version(DuodealerAPI::ApiVersion::NullVersion)
59
+ end
60
+ end
61
+
62
+ test 'two versions with the same version number are equal' do
63
+ version_1 = DuodealerAPI::ApiVersion.new(handle: '2018-09')
64
+ version_2 = DuodealerAPI::ApiVersion.new(handle: '2018-09')
65
+
66
+ assert_equal version_2, version_1
67
+ end
68
+
69
+ test 'two versions with the different version numbers are not equal' do
70
+ version_1 = DuodealerAPI::ApiVersion.new(handle: '2019-07')
71
+ version_2 = DuodealerAPI::ApiVersion.new(handle: '2019-11')
72
+
73
+ refute_equal version_2, version_1
74
+ end
75
+
76
+ test 'versions are ordered by version number with unstable always being the newest' do
77
+ version_1 = DuodealerAPI::ApiVersion.new(handle: '2017-11')
78
+ version_2 = DuodealerAPI::ApiVersion.new(handle: '2019-11')
79
+ version_3 = DuodealerAPI::ApiVersion.new(handle: '2039-01')
80
+ version_4 = DuodealerAPI::ApiVersion.new(handle: '2039-02')
81
+ unstable = DuodealerAPI::ApiVersion.new(handle: :unstable)
82
+
83
+ assert_equal([
84
+ version_1,
85
+ version_2,
86
+ version_3,
87
+ version_4,
88
+ unstable,
89
+ ], [
90
+ version_3,
91
+ version_1,
92
+ version_4,
93
+ unstable,
94
+ version_2,
95
+ ].sort)
96
+ end
97
+
98
+ test 'latest_stable_version will return the version that is newest and stable' do
99
+ assert_equal(
100
+ DuodealerAPI::ApiVersion.versions,
101
+ {
102
+ "2019-01" => DuodealerAPI::ApiVersion.new(handle: '2019-01', supported: true, latest_supported: false),
103
+ "2019-04" => DuodealerAPI::ApiVersion.new(handle: '2019-04', supported: true, latest_supported: false),
104
+ "2019-07" => DuodealerAPI::ApiVersion.new(handle: '2019-07', supported: true, latest_supported: false),
105
+ "2019-10" => DuodealerAPI::ApiVersion.new(handle: '2019-10', supported: false, latest_supported: false),
106
+ "2020-01" => DuodealerAPI::ApiVersion.new(handle: '2020-01', supported: false, latest_supported: true),
107
+ "unstable" => DuodealerAPI::ApiVersion.new(handle: 'unstable', supported: false, latest_supported: false),
108
+ }
109
+ )
110
+ silence_warnings do
111
+
112
+ refute_equal(
113
+ DuodealerAPI::ApiVersion.new(handle: '2019-01'),
114
+ DuodealerAPI::ApiVersion.latest_stable_version
115
+ )
116
+
117
+ assert_equal(
118
+ DuodealerAPI::ApiVersion.new(handle: '2020-01'),
119
+ DuodealerAPI::ApiVersion.latest_stable_version
120
+ )
121
+ end
122
+ end
123
+
124
+ test "NullVersion raises ApiVersionNotSetError" do
125
+ assert_raises(DuodealerAPI::ApiVersion::ApiVersionNotSetError) do
126
+ DuodealerAPI::ApiVersion::NullVersion.construct_api_path(:string)
127
+ end
128
+
129
+ assert_raises(DuodealerAPI::ApiVersion::ApiVersionNotSetError) do
130
+ DuodealerAPI::ApiVersion::NullVersion.construct_graphql_path
131
+ end
132
+
133
+ assert_raises(DuodealerAPI::ApiVersion::ApiVersionNotSetError) do
134
+ DuodealerAPI::ApiVersion::NullVersion.stable?
135
+ end
136
+ end
137
+
138
+ test "NullVersion cannot be instantiated and raises NoMethodError if attempted" do
139
+ assert_raises(NoMethodError) do
140
+ DuodealerAPI::ApiVersion::NullVersion.new
141
+ end
142
+ end
143
+
144
+ test "handle_to_date converts a version handle to a date" do
145
+ version_1 = DuodealerAPI::ApiVersion.new(handle: '2019-01')
146
+ version_2 = DuodealerAPI::ApiVersion.new(handle: 'unstable')
147
+
148
+ assert_equal(version_1.handle_as_date, Time.utc(2019, 01, 01))
149
+ assert_equal(version_2.handle_as_date, DuodealerAPI::ApiVersion::UNSTABLE_AS_DATE)
150
+ end
151
+
152
+ class TestApiVersion < DuodealerAPI::ApiVersion
153
+ def initialize(name)
154
+ @version_name = name
155
+ end
156
+ end
157
+ end