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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Guardfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +50 -0
- data/Rakefile +1 -0
- data/crystal_api.gemspec +34 -0
- data/lib/crystal_api.rb +51 -0
- data/lib/crystal_api/attributes.rb +210 -0
- data/lib/crystal_api/category.rb +28 -0
- data/lib/crystal_api/descriptor.rb +11 -0
- data/lib/crystal_api/error_response.rb +17 -0
- data/lib/crystal_api/errors.rb +8 -0
- data/lib/crystal_api/hmac_request_signing.rb +40 -0
- data/lib/crystal_api/market_prices.rb +11 -0
- data/lib/crystal_api/message_verifier.rb +26 -0
- data/lib/crystal_api/money.rb +8 -0
- data/lib/crystal_api/paginated_collection.rb +13 -0
- data/lib/crystal_api/photo.rb +10 -0
- data/lib/crystal_api/product.rb +55 -0
- data/lib/crystal_api/product_descriptor.rb +10 -0
- data/lib/crystal_api/received_webhook_parser.rb +17 -0
- data/lib/crystal_api/report.rb +17 -0
- data/lib/crystal_api/store.rb +10 -0
- data/lib/crystal_api/store_endpoint.rb +82 -0
- data/lib/crystal_api/store_prefs.rb +54 -0
- data/lib/crystal_api/url.rb +9 -0
- data/lib/crystal_api/variant.rb +28 -0
- data/lib/crystal_api/variant_descriptor.rb +10 -0
- data/lib/crystal_api/version.rb +3 -0
- data/lib/crystal_api/webhook.rb +15 -0
- data/lib/crystal_api/webhook_envelope.rb +12 -0
- data/lib/crystal_api/webhook_registration.rb +34 -0
- data/lib/crystal_api/webhook_verifier.rb +20 -0
- data/spec/cassettes/CrystalApi_StoreEndpoint/_get/prefs/store/makes_the_request_to_the_endpoint.yml +67 -0
- data/spec/cassettes/CrystalApi_StoreEndpoint/_get/prefs/store/parses_the_returned_a_store_pref.yml +67 -0
- data/spec/cassettes/CrystalApi_StoreEndpoint/_get/prefs/store/returns_a_store_pref_instance.yml +67 -0
- data/spec/crystal_api/attributes_spec.rb +7 -0
- data/spec/crystal_api/category_spec.rb +112 -0
- data/spec/crystal_api/money_spec.rb +10 -0
- data/spec/crystal_api/paginated_collection_spec.rb +31 -0
- data/spec/crystal_api/photo_spec.rb +41 -0
- data/spec/crystal_api/product_spec.rb +209 -0
- data/spec/crystal_api/received_webhook_parser_spec.rb +23 -0
- data/spec/crystal_api/report_spec.rb +33 -0
- data/spec/crystal_api/store_endpoint_spec.rb +37 -0
- data/spec/crystal_api/store_prefs_spec.rb +119 -0
- data/spec/crystal_api/store_spec.rb +17 -0
- data/spec/crystal_api/variant_descriptor_spec.rb +16 -0
- data/spec/crystal_api/variant_spec.rb +85 -0
- data/spec/crystal_api/webhook_envelope_spec.rb +45 -0
- data/spec/crystal_api/webhook_registration_spec.rb +129 -0
- data/spec/crystal_api/webhook_spec.rb +54 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/attribute_examples.rb +77 -0
- data/spec/support/vcr.rb +7 -0
- 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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|