crystal_api 0.0.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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +50 -0
  8. data/Rakefile +1 -0
  9. data/crystal_api.gemspec +34 -0
  10. data/lib/crystal_api.rb +51 -0
  11. data/lib/crystal_api/attributes.rb +210 -0
  12. data/lib/crystal_api/category.rb +28 -0
  13. data/lib/crystal_api/descriptor.rb +11 -0
  14. data/lib/crystal_api/error_response.rb +17 -0
  15. data/lib/crystal_api/errors.rb +8 -0
  16. data/lib/crystal_api/hmac_request_signing.rb +40 -0
  17. data/lib/crystal_api/market_prices.rb +11 -0
  18. data/lib/crystal_api/message_verifier.rb +26 -0
  19. data/lib/crystal_api/money.rb +8 -0
  20. data/lib/crystal_api/paginated_collection.rb +13 -0
  21. data/lib/crystal_api/photo.rb +10 -0
  22. data/lib/crystal_api/product.rb +55 -0
  23. data/lib/crystal_api/product_descriptor.rb +10 -0
  24. data/lib/crystal_api/received_webhook_parser.rb +17 -0
  25. data/lib/crystal_api/report.rb +17 -0
  26. data/lib/crystal_api/store.rb +10 -0
  27. data/lib/crystal_api/store_endpoint.rb +82 -0
  28. data/lib/crystal_api/store_prefs.rb +54 -0
  29. data/lib/crystal_api/url.rb +9 -0
  30. data/lib/crystal_api/variant.rb +28 -0
  31. data/lib/crystal_api/variant_descriptor.rb +10 -0
  32. data/lib/crystal_api/version.rb +3 -0
  33. data/lib/crystal_api/webhook.rb +15 -0
  34. data/lib/crystal_api/webhook_envelope.rb +12 -0
  35. data/lib/crystal_api/webhook_registration.rb +34 -0
  36. data/lib/crystal_api/webhook_verifier.rb +20 -0
  37. data/spec/cassettes/CrystalApi_StoreEndpoint/_get/prefs/store/makes_the_request_to_the_endpoint.yml +67 -0
  38. data/spec/cassettes/CrystalApi_StoreEndpoint/_get/prefs/store/parses_the_returned_a_store_pref.yml +67 -0
  39. data/spec/cassettes/CrystalApi_StoreEndpoint/_get/prefs/store/returns_a_store_pref_instance.yml +67 -0
  40. data/spec/crystal_api/attributes_spec.rb +7 -0
  41. data/spec/crystal_api/category_spec.rb +112 -0
  42. data/spec/crystal_api/money_spec.rb +10 -0
  43. data/spec/crystal_api/paginated_collection_spec.rb +31 -0
  44. data/spec/crystal_api/photo_spec.rb +41 -0
  45. data/spec/crystal_api/product_spec.rb +209 -0
  46. data/spec/crystal_api/received_webhook_parser_spec.rb +23 -0
  47. data/spec/crystal_api/report_spec.rb +33 -0
  48. data/spec/crystal_api/store_endpoint_spec.rb +37 -0
  49. data/spec/crystal_api/store_prefs_spec.rb +119 -0
  50. data/spec/crystal_api/store_spec.rb +17 -0
  51. data/spec/crystal_api/variant_descriptor_spec.rb +16 -0
  52. data/spec/crystal_api/variant_spec.rb +85 -0
  53. data/spec/crystal_api/webhook_envelope_spec.rb +45 -0
  54. data/spec/crystal_api/webhook_registration_spec.rb +129 -0
  55. data/spec/crystal_api/webhook_spec.rb +54 -0
  56. data/spec/spec_helper.rb +25 -0
  57. data/spec/support/attribute_examples.rb +77 -0
  58. data/spec/support/vcr.rb +7 -0
  59. metadata +305 -0
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::Photo do
4
+ describe ".from_json" do
5
+ let(:json_hash) do
6
+ {
7
+ "photo" => {
8
+ "urls" => {
9
+ "large" => {
10
+ "href" => "http://client-cdn.crystalcommerce.com/photo/arux/file/75b074b9647299b25110acced18b3943/large/LOTUS_COBRA.png"
11
+ },
12
+ "huge" => {
13
+ "href" => "http://client-cdn.crystalcommerce.com/photo/arux/file/75b074b9647299b25110acced18b3943/huge/LOTUS_COBRA.png"
14
+ },
15
+ "medium" => {
16
+ "href" => "http://client-cdn.crystalcommerce.com/photo/arux/file/75b074b9647299b25110acced18b3943/medium/LOTUS_COBRA.png"
17
+ },
18
+ "thumb" => {
19
+ "href" => "http://client-cdn.crystalcommerce.com/photo/arux/file/75b074b9647299b25110acced18b3943/thumb/LOTUS_COBRA.png"
20
+ },
21
+ "original" => {
22
+ "href" => "http://client-cdn.crystalcommerce.com/photo/arux/file/75b074b9647299b25110acced18b3943/LOTUS_COBRA.PNG"
23
+ }
24
+ },
25
+ "is_default" => true
26
+ }
27
+ }
28
+ end
29
+
30
+ subject { CrystalApi::Photo.from_json(json_hash) }
31
+
32
+ its(:is_default) { should == true }
33
+ its(:urls) { should == {
34
+ "large" => CrystalApi::Url.new(href: "http://client-cdn.crystalcommerce.com/photo/arux/file/75b074b9647299b25110acced18b3943/large/LOTUS_COBRA.png"),
35
+ "huge" => CrystalApi::Url.new(href: "http://client-cdn.crystalcommerce.com/photo/arux/file/75b074b9647299b25110acced18b3943/huge/LOTUS_COBRA.png"),
36
+ "medium" => CrystalApi::Url.new(href: "http://client-cdn.crystalcommerce.com/photo/arux/file/75b074b9647299b25110acced18b3943/medium/LOTUS_COBRA.png"),
37
+ "thumb" => CrystalApi::Url.new(href: "http://client-cdn.crystalcommerce.com/photo/arux/file/75b074b9647299b25110acced18b3943/thumb/LOTUS_COBRA.png"),
38
+ "original" => CrystalApi::Url.new(href: "http://client-cdn.crystalcommerce.com/photo/arux/file/75b074b9647299b25110acced18b3943/LOTUS_COBRA.PNG")
39
+ }}
40
+ end
41
+ end
@@ -0,0 +1,209 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::Product do
4
+ describe ".from_json" do
5
+ let(:json_hash) {{
6
+ 'product' => {
7
+ "_embedded" => {
8
+ "category" => {
9
+ "category" => { "name" => "Zendikar" }
10
+ },
11
+ "market_prices" => {
12
+ "market_prices" => {
13
+ "high_market_price" => { "money" => { "cents" => 624, "currency" => "USD" } },
14
+ "low_market_price" => { "money" => { "cents" => 553, "currency" => "USD" } },
15
+ "market_price" => { "money" => { "cents" => 567, "currency" => "USD" } }
16
+ }
17
+ },
18
+ "variants" => [
19
+ { "variant" => { "id" => 1} },
20
+ { "variant" => { "id" => 2} }
21
+ ]
22
+ },
23
+ "min_sell_price" => {
24
+ "money" => {
25
+ "cents" => 999,
26
+ "currency" => "USD"
27
+ }
28
+ },
29
+ "qty" => 4,
30
+ "name" => "Example Product",
31
+ "catalog_links" => {
32
+ "en" => {
33
+ "href" => "http://bluetest.crystalcommerce.com/catalog/example_category-example_product_category/example_product/385"
34
+ }
35
+ },
36
+ "available_on" => "2012-10-13",
37
+ "base_sell_price" => {
38
+ "money" => {
39
+ "cents" => 999,
40
+ "currency" => "USD"
41
+ }
42
+ },
43
+ "base_buy_price" => {
44
+ "money" => {
45
+ "cents" => 500,
46
+ "currency" => "USD"
47
+ }
48
+ },
49
+ "max_buy_price" => {
50
+ "money" => {
51
+ "cents" => 500,
52
+ "currency" => "USD"
53
+ }
54
+ },
55
+ "photos" => [
56
+ {
57
+ "photo" => {
58
+ "urls" => {
59
+ "original" => {
60
+ "href" => "http://client-cdn.crystalcommerce.com/photo/bluetest/file/31417/smiley.jpg"
61
+ }
62
+ },
63
+ "is_default" => true
64
+ }
65
+ }
66
+ ],
67
+ "max_sell_price" => {
68
+ "money" => {
69
+ "cents" => 999,
70
+ "currency" => "USD"
71
+ }
72
+ },
73
+ "updated_at" => "2012-06-05T16:32:16-07:00",
74
+ "catalog_id" => 55678,
75
+ "_links" => {
76
+ "category" => {
77
+ "href" => "/v1/categories/23"
78
+ },
79
+ "self" => {
80
+ "href" => "/v1/products/385"
81
+ },
82
+ "related_products" => {
83
+ "href" => "/v1/products/385/related"
84
+ }
85
+ },
86
+ "is_preorder" => false,
87
+ "msrp" => {
88
+ "money" => {
89
+ "cents" => 0,
90
+ "currency" => "USD"
91
+ }
92
+ },
93
+ "weight" => 1,
94
+ "possible_variants_count" => 0,
95
+ "related_tag_list" => [
96
+ "!Example Product"
97
+ ],
98
+ "id" => 385,
99
+ "descriptors" => [
100
+ {
101
+ "product_descriptor" => {
102
+ "name" => "Color",
103
+ "value" => "Green"
104
+ }
105
+ },
106
+ {
107
+ "product_descriptor" => {
108
+ "name" => "Flavor Text",
109
+ "value" => ""
110
+ }
111
+ }
112
+ ],
113
+ "is_domestic_only" => false,
114
+ "is_buying" => false,
115
+ "wtb_qty" => 0,
116
+ "variant_dimensions" => ["Condition", "Language"],
117
+ "tag_list" => [
118
+ "!Example Product"
119
+ ],
120
+ "min_buy_price" => {
121
+ "money" => {
122
+ "cents" => 500,
123
+ "currency" => "USD"
124
+ }
125
+ },
126
+ "is_selling" => true,
127
+ "description" => "example description",
128
+ "default_variant" => {
129
+ "variant" => { "id" => 1 }
130
+ },
131
+ "buylist_links" => {
132
+ "en" => {
133
+ "href" => "http://bluetest.crystalcommerce.com/buylist/example_category-example_product_category/example_product/385"
134
+ }
135
+ },
136
+ "seoname" => "example_product"
137
+ }
138
+ }}
139
+
140
+ subject { CrystalApi::Product.from_json(json_hash) }
141
+
142
+ its(:id) { should == 385 }
143
+ its(:catalog_id) { should == 55678 }
144
+ its(:weight) { should == 1.0 }
145
+ its(:name) { should == "Example Product" }
146
+ its(:seoname) { should == "example_product" }
147
+ its(:description) { should == "example description" }
148
+ its(:qty) { should == 4 }
149
+ its(:wtb_qty) { should == 0 }
150
+
151
+ its(:is_buying) { should == false }
152
+ its(:buying?) { should == false }
153
+ its(:is_selling) { should == true }
154
+ its(:selling?) { should == true }
155
+ its(:is_domestic_only) { should == false }
156
+ its(:domestic_only?) { should == false }
157
+ its(:is_preorder) { should == false }
158
+ its(:preorder?) { should == false }
159
+
160
+ its(:msrp) { should == Money.new(0) }
161
+ its(:base_sell_price) { should == Money.new(999) }
162
+ its(:base_buy_price) { should == Money.new(500) }
163
+ its(:min_sell_price) { should == Money.new(999) }
164
+ its(:max_sell_price) { should == Money.new(999) }
165
+ its(:min_buy_price) { should == Money.new(500) }
166
+ its(:max_buy_price) { should == Money.new(500) }
167
+
168
+ its(:updated_at) { should == DateTime.new(2012,6,5,16,32,16, Rational(-7,24)) }
169
+ its(:available_on) { should == Date.new(2012, 10, 13) }
170
+
171
+ its(:default_variant) { should == CrystalApi::Variant.new(id: 1) }
172
+
173
+ its(:related_tag_list) { should == ["!Example Product"] }
174
+ its(:tag_list) { should == ["!Example Product"] }
175
+
176
+ its(:variant_dimensions) { should == ["Condition", "Language"] }
177
+
178
+ its(:photos) { should == [
179
+ CrystalApi::Photo.new(is_default: true,
180
+ urls: {"original" => {href: "http://client-cdn.crystalcommerce.com/photo/bluetest/file/31417/smiley.jpg"}})
181
+ ]}
182
+
183
+ its(:descriptors) { should == [
184
+ CrystalApi::ProductDescriptor.new(name: "Color", value: "Green"),
185
+ CrystalApi::ProductDescriptor.new(name: "Flavor Text", value: "")
186
+ ]}
187
+
188
+ its(:catalog_links) { should == {
189
+ "en" => CrystalApi::Url.new(href: "http://bluetest.crystalcommerce.com/catalog/example_category-example_product_category/example_product/385")
190
+ }}
191
+
192
+ its(:buylist_links) { should == {
193
+ "en" => CrystalApi::Url.new(href: "http://bluetest.crystalcommerce.com/buylist/example_category-example_product_category/example_product/385")
194
+ }}
195
+
196
+ its(:category) { should == CrystalApi::Category.new(name: "Zendikar")}
197
+
198
+ its(:variants) { should == [
199
+ CrystalApi::Variant.new(id: 1),
200
+ CrystalApi::Variant.new(id: 2)
201
+ ]}
202
+
203
+ its(:market_prices) {
204
+ should == CrystalApi::MarketPrices.new(high_market_price: Money.new(624),
205
+ low_market_price: Money.new(553),
206
+ market_price: Money.new(567))
207
+ }
208
+ end
209
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::ReceivedWebhookParser do
4
+ let(:raw_post) {{'foo' => 'bar'}.to_json}
5
+ let(:webhook_envelope) { mock("WebhookEnvelope") }
6
+
7
+ before(:each) do
8
+ CrystalApi::WebhookEnvelope.stub(:new).and_return(webhook_envelope)
9
+ end
10
+
11
+ subject { CrystalApi::ReceivedWebhookParser.new(raw_post) }
12
+
13
+ describe "#webhook" do
14
+ it "instantiates a new webhook envelope with the parsed raw post" do
15
+ CrystalApi::WebhookEnvelope.should_receive(:from_json).with({'foo' => 'bar'})
16
+ subject.webhook
17
+ end
18
+
19
+ it "returns the instantiated webhook envelope" do
20
+ subject.webhook.should == webhook_envelope
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::Report do
4
+ describe ".from_json" do
5
+ let(:json_hash) {{
6
+ "report" => {
7
+ "id" => 123,
8
+ "status" => "pending",
9
+ "report_type" => "inventory_report",
10
+ "updated_at" => "2012-06-05T16:32:16-07:00",
11
+ "created_at" => "2012-06-05T16:32:16-07:00",
12
+ "expires_at" => "2012-06-05T16:32:16-07:00",
13
+ "_links" => {
14
+ "download" => {"href" => "download.com"},
15
+ "self" => {"href" => "self.com"}
16
+ }
17
+ }
18
+ }}
19
+
20
+ subject { CrystalApi::Report.from_json(json_hash) }
21
+
22
+ its(:id) { should == 123 }
23
+ its(:status) { should == "pending" }
24
+ its(:report_type) { should == "inventory_report" }
25
+ its(:updated_at) { should == DateTime.new(2012,6,5,16,32,16, Rational(-7,24)) }
26
+ its(:created_at) { should == DateTime.new(2012,6,5,16,32,16, Rational(-7,24)) }
27
+ its(:expires_at) { should == DateTime.new(2012,6,5,16,32,16, Rational(-7,24)) }
28
+ its(:_links) { should == {
29
+ "download" => CrystalApi::Url.new(href: "download.com"),
30
+ "self" => CrystalApi::Url.new(href: "self.com")
31
+ }}
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::StoreEndpoint do
4
+ subject { CrystalApi::StoreEndpoint.new(base_url: "http://localhost:3000/api/v1",
5
+ token: "TOKEN") }
6
+
7
+ its(:base_url) { should == "http://localhost:3000/api/v1" }
8
+ its(:token) { should == "TOKEN" }
9
+ its(:headers) { should include("Authorization" => "OAuth TOKEN") }
10
+
11
+ describe "#get" do
12
+ context "/prefs/store", :vcr do
13
+ it "makes the request to the endpoint" do
14
+ subject.get("/prefs/store")
15
+ a_request(:get, "http://localhost:3000/api/v1/prefs/store").
16
+ should have_been_made
17
+ end
18
+
19
+ it "parses the returned a store pref" do
20
+ subject.get("/prefs/store").parsed.should be_a(CrystalApi::StorePrefs)
21
+ end
22
+ end
23
+
24
+ context "csv parsing" do
25
+ before do
26
+ stub_request(:get, "http://localhost:3000/api/v1/reports/1234/download").
27
+ to_return(:body => "id,name\n1,Fred",
28
+ :headers => {:content_type => "text/csv"})
29
+ end
30
+
31
+ it "parses the response as a CSV" do
32
+ response = subject.get("/reports/1234/download")
33
+ response.parsed.should be_a(CSV::Table)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::StorePrefs do
4
+ describe ".from_json" do
5
+ let(:json_hash) do
6
+ JSON.parse(<<-STR)
7
+ {
8
+ "store_prefs": {
9
+ "buylist_email": "donald@crystalcommerce.com",
10
+ "satisfaction_blurb": "Our #1 goal is making sure you''re happy with your purchase today. Happy customers come back soon and tell their friends. Its a win, win.",
11
+ "enable_frontend_auto_translate": false,
12
+ "logo_photo": {
13
+ "photo": {
14
+ "is_default": false,
15
+ "urls": {
16
+ "original": {
17
+ "href": "http:\/\/crystalcommerce-development.s3.amazonaws.com\/photo\/arux\/file\/8e233b1f3da489341a9d55a1ca744556\/arux-banner.png"
18
+ }
19
+ }
20
+ }
21
+ },
22
+ "return_blurb": "If your order ever arrives in less than expected condition, please promptly return it to us for a full refund. We take your business with us very seriously.",
23
+ "time_zone": "Pacific Time (US & Canada)",
24
+ "city": "Seattle",
25
+ "hostname": "www.example.com",
26
+ "customer_service_phone": "555-555-5555",
27
+ "zip": "98125",
28
+ "sales_tax": "9",
29
+ "cancel": "14",
30
+ "default_currency_code": "USD",
31
+ "default_locale": "x-bork",
32
+ "privacy_blurb": "We promise to never ever sell your personal private information to any 3rd party company! We hate spam too. Order today with confidence.",
33
+ "enable_manual_credit_card_capture": false,
34
+ "store_credit_multiplier": "1.3",
35
+ "buylist_cancel_days": "0",
36
+ "checkout_special_instructions": "",
37
+ "default_exchange_rate": "1",
38
+ "enable_buy_order_notifications": "1",
39
+ "enable_buylist": true,
40
+ "enable_bcc_buylist_confirmation_emails": false,
41
+ "enable_tax_on_shipping": true,
42
+ "enable_sort_by_price": true,
43
+ "feedback_timespan": "14",
44
+ "home_redirect": "\/catalog\/magic_singles-core_sets-magic_2012\/306",
45
+ "phone": "555-555-5555",
46
+ "state": "WA",
47
+ "enable_referafriend": true,
48
+ "address": "3400 NE 110th ST #B-1",
49
+ "payment": "7",
50
+ "contact_name": "Mr. Arux",
51
+ "name": "Arux Gaming and Hobbies",
52
+ "display_customer_service_phone": false,
53
+ "enable_order_notifications": "2",
54
+ "enable_account_approval": false,
55
+ "enable_watermarking_of_product_photos": false,
56
+ "watermark_photo": null,
57
+ "country": "US",
58
+ "contact_email": "donald@crystalcommerce.com",
59
+ "buylist_reminder_days": "0",
60
+ "hide_bank_transfer_details_in_emails": false,
61
+ "enable_invoice_logo": false
62
+ }
63
+ }
64
+ STR
65
+ end
66
+
67
+ subject { CrystalApi::StorePrefs.from_json(json_hash) }
68
+
69
+ its(:hostname) { should == "www.example.com" }
70
+ its(:city) { should == "Seattle" }
71
+
72
+ its(:return_blurb) { should == "If your order ever arrives in less than expected condition, please promptly return it to us for a full refund. We take your business with us very seriously." }
73
+ its(:time_zone) { should == "Pacific Time (US & Canada)" }
74
+ its(:customer_service_phone) { should == "555-555-5555" }
75
+ its(:zip) { should == "98125" }
76
+ its(:sales_tax) { should == "9" }
77
+ its(:cancel) { should == "14" }
78
+ its(:default_currency_code) { should == "USD" }
79
+ its(:default_locale) { should == "x-bork" }
80
+ its(:privacy_blurb) { should == "We promise to never ever sell your personal private information to any 3rd party company! We hate spam too. Order today with confidence." }
81
+ its(:enable_manual_credit_card_capture) { should == false }
82
+ its(:store_credit_multiplier) { should == "1.3" }
83
+ its(:buylist_cancel_days) { should == "0" }
84
+ its(:checkout_special_instructions) { should == "" }
85
+ its(:default_exchange_rate) { should == "1" }
86
+ its(:enable_buy_order_notifications) { should == "1" }
87
+ its(:enable_buylist) { should == true }
88
+ its(:enable_bcc_buylist_confirmation_emails) { should == false }
89
+ its(:enable_tax_on_shipping) { should == true }
90
+ its(:enable_sort_by_price) { should == true }
91
+ its(:feedback_timespan) { should == "14" }
92
+ its(:home_redirect) { should == "\/catalog\/magic_singles-core_sets-magic_2012\/306" }
93
+ its(:phone) { should == "555-555-5555" }
94
+ its(:state) { should == "WA" }
95
+ its(:enable_referafriend) { should == true }
96
+ its(:address) { should == "3400 NE 110th ST #B-1" }
97
+ its(:payment) { should == "7" }
98
+ its(:contact_name) { should == "Mr. Arux" }
99
+ its(:name) { should == "Arux Gaming and Hobbies" }
100
+ its(:display_customer_service_phone) { should == false }
101
+ its(:enable_order_notifications) { should == "2" }
102
+ its(:enable_account_approval) { should == false }
103
+ its(:enable_watermarking_of_product_photos) { should == false }
104
+ its(:watermark_photo) { should == nil }
105
+ its(:country) { should == "US" }
106
+ its(:contact_email) { should == "donald@crystalcommerce.com" }
107
+ its(:buylist_reminder_days) { should == "0" }
108
+ its(:hide_bank_transfer_details_in_emails) { should == false }
109
+ its(:enable_invoice_logo) { should == false}
110
+ its(:buylist_email) { should == "donald@crystalcommerce.com" }
111
+ its(:satisfaction_blurb) { should == "Our #1 goal is making sure you''re happy with your purchase today. Happy customers come back soon and tell their friends. Its a win, win." }
112
+ its(:enable_frontend_auto_translate) { should == false }
113
+
114
+ its(:logo_photo) { should ==
115
+ CrystalApi::Photo.new(is_default: false,
116
+ urls: {"original" => {href: "http://crystalcommerce-development.s3.amazonaws.com/photo/arux/file/8e233b1f3da489341a9d55a1ca744556/arux-banner.png"}})
117
+ }
118
+ end
119
+ end