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,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