crystal_api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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