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,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::Store do
4
+ describe "#from_json" do
5
+ let(:json_hash) {{
6
+ "store" => {
7
+ "name" => "Arux Gaming Store",
8
+ "database_name" => "arux"
9
+ }
10
+ }}
11
+
12
+ subject { CrystalApi::Store.from_json(json_hash) }
13
+
14
+ its(:name) { should == "Arux Gaming Store" }
15
+ its(:database_name) { should == "arux" }
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::VariantDescriptor do
4
+ describe ".from_json" do
5
+ let(:json_hash) {{
6
+ 'variant_descriptor' => {
7
+ 'name' => 'Condition',
8
+ 'value' => 'Near Mint'
9
+ }
10
+ }}
11
+
12
+ subject { CrystalApi::VariantDescriptor.from_json(json_hash) }
13
+ its(:name) { should == 'Condition' }
14
+ its(:value) { should == 'Near Mint' }
15
+ end
16
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::Variant do
4
+ describe "#id" do
5
+ let(:attribute) { :id }
6
+ it_should_behave_like "integer attribute"
7
+ end
8
+
9
+ describe "#product_id" do
10
+ let(:attribute) { :product_id }
11
+ it_should_behave_like "integer attribute"
12
+ end
13
+
14
+ describe "#qty" do
15
+ let(:attribute) { :qty }
16
+ it_should_behave_like "integer attribute"
17
+ end
18
+
19
+ describe "#wtb_qty" do
20
+ let(:attribute) { :wtb_qty }
21
+ it_should_behave_like "integer attribute"
22
+ end
23
+
24
+ describe "#catalog_id" do
25
+ let(:attribute) { :catalog_id }
26
+ it_should_behave_like "string attribute"
27
+ end
28
+
29
+ describe "#is_default" do
30
+ let(:attribute) { :is_default }
31
+ it_should_behave_like "boolean attribute"
32
+
33
+ it "defines the default? method" do
34
+ CrystalApi::Variant.new(is_default: true).default?.should == true
35
+ end
36
+ end
37
+
38
+ describe "#is_infinite_qty" do
39
+ let(:attribute) { :is_infinite_qty }
40
+ it_should_behave_like "boolean attribute"
41
+
42
+ it "defines the infinite_qty? method" do
43
+ CrystalApi::Variant.new(is_infinite_qty: true).infinite_qty?.should == true
44
+ end
45
+ end
46
+
47
+ describe ".from_json" do
48
+ let(:json_hash) {{
49
+ 'variant' => {
50
+ 'id' => 123,
51
+ 'is_default' => false,
52
+ 'is_infinite_qty' => false,
53
+ 'product_id' => 12,
54
+ 'qty' => 2,
55
+ 'wtb_qty' => 8,
56
+ 'sell_price' => {'money' => {'cents' => 500, 'currency' => 'USD'}},
57
+ 'buy_price' => {'money' => {'cents' => 100, 'currency' => 'USD'}},
58
+ 'store_credit_buy_price' => {'money' => {'cents' => 130, 'currency' => 'USD'}},
59
+ 'catalog_id' => 'skusku',
60
+ 'product_catalog_id' => 55,
61
+ 'product_name' => "Lotus Cobra",
62
+ 'category_name' => "Zendikar",
63
+ 'descriptors' => [
64
+ {
65
+ 'variant_descriptor' => {
66
+ 'name' => 'Condition',
67
+ 'value' => 'Near Mint',
68
+ }
69
+ }
70
+ ]
71
+ }}}
72
+ subject { CrystalApi::Variant.from_json(json_hash) }
73
+ its(:id) { should == 123 }
74
+ its(:product_id) { should == 12 }
75
+ its(:descriptors) { should == [
76
+ CrystalApi::VariantDescriptor.new(name: 'Condition', value: 'Near Mint')
77
+ ] }
78
+ its(:sell_price) { should == Money.new(500) }
79
+ its(:buy_price) { should == Money.new(100) }
80
+ its(:store_credit_buy_price) { should == Money.new(130) }
81
+ its(:product_catalog_id) { should == 55 }
82
+ its(:product_name) { should == "Lotus Cobra" }
83
+ its(:category_name) { should == "Zendikar" }
84
+ end
85
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::WebhookEnvelope do
4
+
5
+ subject { CrystalApi::WebhookEnvelope.new }
6
+
7
+ describe "#from_json" do
8
+ let(:json_hash) {{
9
+ "webhook_envelope" => {
10
+ "topic" => "product/update",
11
+ "store_name" => "examplestore",
12
+ "resource_id" => 385,
13
+ "payload" => {'variant' => {'id' => 1}}}
14
+ }}
15
+
16
+ subject { CrystalApi::WebhookEnvelope.from_json(json_hash) }
17
+
18
+ its(:topic) { should == "product/update" }
19
+ its(:store_name) { should == "examplestore"}
20
+ its(:resource_id) { should == 385 }
21
+ its(:payload) { should be_a(CrystalApi::Variant) }
22
+
23
+ context "multiple items in the payload" do
24
+ let(:json_hash) {{
25
+ "webhook_envelope" => {
26
+ "topic" => "product/update",
27
+ "store_name" => "examplestore",
28
+ "resource_id" => 385,
29
+ "payload" => [
30
+ {'variant' => {'id' => 1}},
31
+ {'variant' => {'id' => 2}}
32
+ ]}
33
+ }}
34
+
35
+ subject { CrystalApi::WebhookEnvelope.from_json(json_hash) }
36
+
37
+ it "has both variants in the payload" do
38
+ subject.payload.should == [
39
+ CrystalApi::Variant.new(id: 1),
40
+ CrystalApi::Variant.new(id: 2)
41
+ ]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::WebhookRegistration do
4
+ let(:store_endpoint) { mock("Store Endpoint") }
5
+
6
+ subject { CrystalApi::WebhookRegistration.new(store_endpoint) }
7
+
8
+ describe "#register" do
9
+ let(:parsed_response) { mock("Parsed Response") }
10
+ let(:webhook) { mock("Webhook", :to_json => "{}") }
11
+
12
+ before(:each) do
13
+ store_endpoint.stub(:post).and_return(parsed_response)
14
+ end
15
+
16
+ it "configures the json to exclude the id in the body" do
17
+ webhook.should_receive(:to_json).with(except: [:id])
18
+ subject.register(webhook)
19
+ end
20
+
21
+ it "posts the webhook json to the store endpoint" do
22
+ store_endpoint.should_receive(:post).with('/webhooks', "{}")
23
+ subject.register(webhook)
24
+ end
25
+
26
+ it "returns the parsed response" do
27
+ subject.register(webhook).should == parsed_response
28
+ end
29
+ end
30
+
31
+ describe "#registered?" do
32
+ let(:parsed_response) { mock("Parsed Response", :parsed => existing_webhooks) }
33
+ let(:existing_webhooks) { [] }
34
+ let(:existing_webhook) { mock("Webhook", address: 'url',
35
+ topic: 'pages/update',
36
+ resource_id: nil,
37
+ id: 11) }
38
+ let(:webhook) { mock("Webhook", address: 'url',
39
+ topic: 'pages/update',
40
+ resource_id: nil) }
41
+
42
+ before(:each) do
43
+ store_endpoint.stub(:get).and_return(parsed_response)
44
+ end
45
+
46
+ it "gets all the existing webhooks" do
47
+ store_endpoint.should_receive(:get).with('/webhooks')
48
+ subject.registered?(webhook)
49
+ end
50
+
51
+ context "one of the returned webhooks the webhook we already have" do
52
+ let(:existing_webhooks) { [existing_webhook] }
53
+
54
+ it "returns true" do
55
+ subject.registered?(webhook).should be_true
56
+ end
57
+ end
58
+
59
+ context "the webhook we want is not registered" do
60
+ it "returns false" do
61
+ subject.registered?(webhook).should be_false
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "#deregister" do
67
+ it "makes a delete request to the endpoint" do
68
+ store_endpoint.should_receive(:delete).with("/webhooks/11")
69
+ subject.deregister(11)
70
+ end
71
+ end
72
+
73
+ describe "registered_webhooks" do
74
+ let(:parsed_response) { mock("Parsed Response", :parsed => existing_webhooks) }
75
+ let(:existing_webhooks) { [existing_webhook] }
76
+ let(:existing_webhook) { mock("Webhook", address: 'url',
77
+ topic: 'pages/update',
78
+ resource_id: nil,
79
+ id: 11) }
80
+
81
+ before(:each) do
82
+ store_endpoint.stub(:get).and_return(parsed_response)
83
+ end
84
+
85
+ it "gets all the existing webhooks" do
86
+ store_endpoint.should_receive(:get).with('/webhooks')
87
+ subject.registered_webhooks
88
+ end
89
+
90
+ it "returns the existing webhooks" do
91
+ subject.registered_webhooks.should == existing_webhooks
92
+ end
93
+ end
94
+
95
+ describe "#webhook_id" do
96
+ let(:parsed_response) { mock("Parsed Response", :parsed => existing_webhooks) }
97
+ let(:existing_webhooks) { [] }
98
+ let(:existing_webhook) { mock("Webhook", address: 'url',
99
+ topic: 'pages/update',
100
+ resource_id: nil,
101
+ id: 11) }
102
+ let(:webhook) { mock("Webhook", address: 'url',
103
+ topic: 'pages/update',
104
+ resource_id: nil) }
105
+
106
+ before(:each) do
107
+ store_endpoint.stub(:get).and_return(parsed_response)
108
+ end
109
+
110
+ it "gets all the existing webhooks" do
111
+ store_endpoint.should_receive(:get).with('/webhooks')
112
+ subject.webhook_id(webhook)
113
+ end
114
+
115
+ context "one of the returned webhooks the webhook we already have" do
116
+ let(:existing_webhooks) { [existing_webhook] }
117
+
118
+ it "returns true" do
119
+ subject.webhook_id(webhook).should == 11
120
+ end
121
+ end
122
+
123
+ context "the webhook we want is not registered" do
124
+ it "returns false" do
125
+ subject.webhook_id(webhook).should == nil
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrystalApi::Webhook do
4
+ describe ".from_json" do
5
+ let(:json_hash) {{
6
+ "webhook" => {
7
+ "address" => "https://example.com/cb",
8
+ "resource_id" => 123,
9
+ "topic" => "pages/create",
10
+ "id" => 11,
11
+ "only_catalog" => true
12
+ }
13
+ }}
14
+
15
+ subject { CrystalApi::Webhook.from_json(json_hash) }
16
+
17
+ its(:address) { should == "https://example.com/cb" }
18
+ its(:resource_id) { should == 123 }
19
+ its(:topic) { should == "pages/create" }
20
+ its(:id) { should == 11 }
21
+ its(:only_catalog) { should == true }
22
+ end
23
+
24
+ describe "#as_json" do
25
+ subject { CrystalApi::Webhook.new(address: "https://example.com/cb",
26
+ resource_id: 123,
27
+ topic: "pages/create",
28
+ id: 1,
29
+ only_catalog: true) }
30
+
31
+ its(:as_json) { should == {
32
+ "webhook" => {
33
+ "address" => "https://example.com/cb",
34
+ "resource_id" => 123,
35
+ "topic" => "pages/create",
36
+ "id" => 1,
37
+ "only_catalog" => true
38
+ }
39
+ }}
40
+
41
+ describe "except key" do
42
+ it "ignores the key expected" do
43
+ subject.as_json(:except => [:id]).should == {
44
+ "webhook" => {
45
+ "address" => "https://example.com/cb",
46
+ "resource_id" => 123,
47
+ "topic" => "pages/create",
48
+ "only_catalog" => true
49
+ }
50
+ }
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,25 @@
1
+ require_relative '../lib/crystal_api'
2
+ # This file was generated by the `rspec --init` command. Conventionally, all
3
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
4
+ # Require this file using `require "spec_helper"` to ensure that it is only
5
+ # loaded once.
6
+ #
7
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
8
+ RSpec.configure do |config|
9
+ config.treat_symbols_as_metadata_keys_with_true_values = true
10
+ config.run_all_when_everything_filtered = true
11
+ config.filter_run :focus
12
+
13
+ # Run specs in random order to surface order dependencies. If you find an
14
+ # order dependency and want to debug it, you can fix the order by providing
15
+ # the seed, which is printed after each run.
16
+ # --seed 1234
17
+ config.order = 'random'
18
+
19
+ require 'webmock/rspec'
20
+ require_relative 'support/attribute_examples'
21
+ require_relative 'support/vcr'
22
+
23
+ # in RSpec 3 this will no longer be necessary.
24
+ config.treat_symbols_as_metadata_keys_with_true_values = true
25
+ end
@@ -0,0 +1,77 @@
1
+ shared_examples_for "integer attribute" do
2
+ context "string argument" do
3
+ subject { CrystalApi::Variant.new("#{attribute}" => "5") }
4
+
5
+ it "parses as integer" do
6
+ subject.send(attribute).should == 5
7
+ end
8
+ end
9
+
10
+ context "integer argument" do
11
+ subject { CrystalApi::Variant.new("#{attribute}" => 5) }
12
+
13
+ it "parses as integer" do
14
+ subject.send(attribute).should == 5
15
+ end
16
+ end
17
+ end
18
+
19
+ shared_examples_for "string attribute" do
20
+ context "string argument" do
21
+ subject { CrystalApi::Variant.new("#{attribute}" => "str") }
22
+
23
+ it "parses as string" do
24
+ subject.send(attribute).should == "str"
25
+ end
26
+ end
27
+ end
28
+
29
+ shared_examples_for "boolean attribute" do
30
+ context "argument 'true'" do
31
+ subject { CrystalApi::Variant.new("#{attribute}" => "true") }
32
+
33
+ it "parses as boolean" do
34
+ subject.send(attribute).should == true
35
+ end
36
+ end
37
+
38
+ context "argument 'false'" do
39
+ subject { CrystalApi::Variant.new("#{attribute}" => "false") }
40
+
41
+ it "parses as boolean" do
42
+ subject.send(attribute).should == false
43
+ end
44
+ end
45
+
46
+ context "argument '1'" do
47
+ subject { CrystalApi::Variant.new("#{attribute}" => "1") }
48
+
49
+ it "parses as boolean" do
50
+ subject.send(attribute).should == true
51
+ end
52
+ end
53
+
54
+ context "argument '0'" do
55
+ subject { CrystalApi::Variant.new("#{attribute}" => "0") }
56
+
57
+ it "parses as boolean" do
58
+ subject.send(attribute).should == false
59
+ end
60
+ end
61
+
62
+ context "argument true" do
63
+ subject { CrystalApi::Variant.new("#{attribute}" => true) }
64
+
65
+ it "parses as boolean" do
66
+ subject.send(attribute).should == true
67
+ end
68
+ end
69
+
70
+ context "argument false" do
71
+ subject { CrystalApi::Variant.new("#{attribute}" => false) }
72
+
73
+ it "parses as boolean" do
74
+ subject.send(attribute).should == false
75
+ end
76
+ end
77
+ end