crystal_api 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,40 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module CrystalApi
|
6
|
+
module HmacRequestSigning
|
7
|
+
def inject_signature_header(headers, signature)
|
8
|
+
headers['X-Hmac-Sha256'] = signature
|
9
|
+
headers
|
10
|
+
end
|
11
|
+
|
12
|
+
def request_signature(request)
|
13
|
+
request.headers['X_HMAC_SHA256']
|
14
|
+
end
|
15
|
+
|
16
|
+
def hmac_sha256(key, message)
|
17
|
+
digest = OpenSSL::HMAC.digest('sha256', key, message)
|
18
|
+
# chomp -- the base64 encoded version will have a newline at the end
|
19
|
+
Base64.encode64(digest).chomp
|
20
|
+
end
|
21
|
+
|
22
|
+
def sign_params(verb, host, path, secret_key, params)
|
23
|
+
hmac_sha256(secret_key, serialized_params(verb, host, path, params))
|
24
|
+
end
|
25
|
+
|
26
|
+
def serialized_params(verb, host, path, params)
|
27
|
+
[ verb.to_s.upcase,
|
28
|
+
host,
|
29
|
+
path,
|
30
|
+
canonicalize_params(params) ].join("\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
def canonicalize_params(params)
|
34
|
+
# Make sure we have string keys, otherwise the sort does not work
|
35
|
+
params.keys.sort.map do |key|
|
36
|
+
[CGI.escape(key.to_s), CGI.escape(params[key].to_s)].join('=')
|
37
|
+
end.join('&')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module CrystalApi
|
2
|
+
class MessageVerifier
|
3
|
+
include HmacRequestSigning
|
4
|
+
|
5
|
+
attr_reader :shared_secret, :request
|
6
|
+
|
7
|
+
def initialize(shared_secret, request)
|
8
|
+
@shared_secret = shared_secret
|
9
|
+
@request = request
|
10
|
+
end
|
11
|
+
|
12
|
+
def verified?
|
13
|
+
request_signature(request) == expected_signature
|
14
|
+
end
|
15
|
+
|
16
|
+
def expected_signature
|
17
|
+
rack_request = Rack::Request.new(request.env)
|
18
|
+
|
19
|
+
sign_params(rack_request.request_method,
|
20
|
+
rack_request.host,
|
21
|
+
rack_request.path,
|
22
|
+
shared_secret,
|
23
|
+
rack_request.params)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module CrystalApi
|
2
|
+
class PaginatedCollection
|
3
|
+
include Attributes
|
4
|
+
|
5
|
+
root_element :paginated_collection
|
6
|
+
|
7
|
+
integer_attribute :total_entries
|
8
|
+
integer_attribute :total_pages
|
9
|
+
integer_attribute :previous_page
|
10
|
+
integer_attribute :next_page
|
11
|
+
array_attribute :entries
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module CrystalApi
|
2
|
+
class Product
|
3
|
+
include Attributes
|
4
|
+
|
5
|
+
root_element :product
|
6
|
+
|
7
|
+
integer_attribute :id
|
8
|
+
integer_attribute :catalog_id
|
9
|
+
|
10
|
+
string_attribute :name
|
11
|
+
string_attribute :seoname
|
12
|
+
string_attribute :description
|
13
|
+
|
14
|
+
decimal_attribute :weight
|
15
|
+
|
16
|
+
integer_attribute :qty
|
17
|
+
integer_attribute :wtb_qty
|
18
|
+
integer_attribute :max_qty
|
19
|
+
|
20
|
+
boolean_attribute :is_buying
|
21
|
+
boolean_attribute :is_selling
|
22
|
+
boolean_attribute :is_domestic_only
|
23
|
+
boolean_attribute :is_preorder
|
24
|
+
|
25
|
+
object_attribute :msrp
|
26
|
+
object_attribute :base_sell_price
|
27
|
+
object_attribute :base_buy_price
|
28
|
+
object_attribute :min_sell_price
|
29
|
+
object_attribute :max_sell_price
|
30
|
+
object_attribute :min_buy_price
|
31
|
+
object_attribute :max_buy_price
|
32
|
+
|
33
|
+
datetime_attribute :updated_at
|
34
|
+
date_attribute :available_on
|
35
|
+
|
36
|
+
object_attribute :default_variant
|
37
|
+
|
38
|
+
array_attribute :related_tag_list, :string
|
39
|
+
array_attribute :tag_list, :string
|
40
|
+
|
41
|
+
array_attribute :variant_dimensions, :string
|
42
|
+
|
43
|
+
array_attribute :photos, :object
|
44
|
+
array_attribute :descriptors, :object
|
45
|
+
|
46
|
+
hash_attribute :catalog_links, :url
|
47
|
+
hash_attribute :buylist_links, :url
|
48
|
+
|
49
|
+
embedded_attribute :category
|
50
|
+
|
51
|
+
embedded_attribute :variants, :array
|
52
|
+
|
53
|
+
embedded_attribute :market_prices
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CrystalApi
|
2
|
+
class ReceivedWebhookParser
|
3
|
+
attr_reader :raw_post
|
4
|
+
|
5
|
+
def initialize(raw_post)
|
6
|
+
@raw_post = raw_post
|
7
|
+
end
|
8
|
+
|
9
|
+
def webhook
|
10
|
+
WebhookEnvelope.from_json(json)
|
11
|
+
end
|
12
|
+
|
13
|
+
def json
|
14
|
+
@json ||= MultiJson.load(raw_post)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CrystalApi
|
2
|
+
class Report
|
3
|
+
include Attributes
|
4
|
+
|
5
|
+
root_element :report
|
6
|
+
|
7
|
+
integer_attribute :id
|
8
|
+
string_attribute :status
|
9
|
+
string_attribute :report_type
|
10
|
+
|
11
|
+
datetime_attribute :created_at
|
12
|
+
datetime_attribute :expires_at
|
13
|
+
datetime_attribute :updated_at
|
14
|
+
|
15
|
+
hash_attribute :_links, :url
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'values'
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
module CrystalApi
|
6
|
+
class StoreEndpoint
|
7
|
+
attr_reader :base_url, :token
|
8
|
+
|
9
|
+
class Response < Value.new(:success, :parsed, :raw, :json)
|
10
|
+
alias_method :to_hash, :json
|
11
|
+
alias_method :success?, :success
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"<CrystalApi::StoreEndpoint::Response parsed:#{parsed.class.name} raw.length:#{raw.length}>"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(args = {})
|
19
|
+
@base_url = args[:base_url]
|
20
|
+
@token = args[:token]
|
21
|
+
end
|
22
|
+
|
23
|
+
def headers
|
24
|
+
{
|
25
|
+
"Authorization" => "OAuth #{token}",
|
26
|
+
"Accept" => "application/json",
|
27
|
+
"Content-Type" => "application/json",
|
28
|
+
"X-Requested-With" => "XMLHttpRequest"
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
[:get, :delete, :post, :put].each do |method|
|
33
|
+
define_method(method) do |*args|
|
34
|
+
options = {:headers => headers}
|
35
|
+
if args.length == 2
|
36
|
+
options[:body] = args[1].to_json
|
37
|
+
end
|
38
|
+
|
39
|
+
path = args[0]
|
40
|
+
url = base_url + path
|
41
|
+
|
42
|
+
raw = HTTParty.public_send(method, url, options)
|
43
|
+
wrap_response(raw)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def wrap_response(raw)
|
50
|
+
if raw.content_type =~ /application\/json/
|
51
|
+
json = JSON.parse(raw.body)
|
52
|
+
if raw.code == 200
|
53
|
+
parsed = parse_response_body(json)
|
54
|
+
else
|
55
|
+
parsed = error_response(json)
|
56
|
+
end
|
57
|
+
elsif raw.content_type =~ /text\/csv/
|
58
|
+
parsed = CSV.parse(raw.body, :headers => true)
|
59
|
+
end
|
60
|
+
|
61
|
+
Response.new(raw.code == 200, parsed, raw, json)
|
62
|
+
end
|
63
|
+
|
64
|
+
def error_response(json)
|
65
|
+
ErrorResponse.new(json)
|
66
|
+
end
|
67
|
+
|
68
|
+
def parse_response_body(json)
|
69
|
+
if json.is_a?(Array)
|
70
|
+
json.map {|obj| parse(obj)}
|
71
|
+
else
|
72
|
+
parse(json)
|
73
|
+
end
|
74
|
+
rescue JSON::ParserError
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def parse(object)
|
79
|
+
CrystalApi.from_json(object)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module CrystalApi
|
2
|
+
class StorePrefs
|
3
|
+
include Attributes
|
4
|
+
|
5
|
+
root_element :store_prefs
|
6
|
+
|
7
|
+
string_attribute :city
|
8
|
+
string_attribute :hostname
|
9
|
+
string_attribute :return_blurb
|
10
|
+
string_attribute :time_zone
|
11
|
+
string_attribute :customer_service_phone
|
12
|
+
string_attribute :zip
|
13
|
+
string_attribute :sales_tax
|
14
|
+
string_attribute :cancel
|
15
|
+
string_attribute :default_currency_code
|
16
|
+
string_attribute :default_locale
|
17
|
+
string_attribute :privacy_blurb
|
18
|
+
string_attribute :store_credit_multiplier
|
19
|
+
string_attribute :buylist_cancel_days
|
20
|
+
string_attribute :checkout_special_instructions
|
21
|
+
string_attribute :default_exchange_rate
|
22
|
+
string_attribute :feedback_timespan
|
23
|
+
string_attribute :home_redirect
|
24
|
+
string_attribute :phone
|
25
|
+
string_attribute :state
|
26
|
+
string_attribute :address
|
27
|
+
string_attribute :payment
|
28
|
+
string_attribute :contact_name
|
29
|
+
string_attribute :name
|
30
|
+
string_attribute :watermark_photo
|
31
|
+
string_attribute :country
|
32
|
+
string_attribute :contact_email
|
33
|
+
string_attribute :buylist_reminder_days
|
34
|
+
string_attribute :enable_order_notifications
|
35
|
+
string_attribute :enable_buy_order_notifications
|
36
|
+
string_attribute :buylist_email
|
37
|
+
string_attribute :satisfaction_blurb
|
38
|
+
|
39
|
+
boolean_attribute :display_customer_service_phone
|
40
|
+
boolean_attribute :hide_bank_transfer_details_in_emails
|
41
|
+
boolean_attribute :enable_manual_credit_card_capture
|
42
|
+
boolean_attribute :enable_buylist
|
43
|
+
boolean_attribute :enable_bcc_buylist_confirmation_emails
|
44
|
+
boolean_attribute :enable_tax_on_shipping
|
45
|
+
boolean_attribute :enable_sort_by_price
|
46
|
+
boolean_attribute :enable_referafriend
|
47
|
+
boolean_attribute :enable_account_approval
|
48
|
+
boolean_attribute :enable_watermarking_of_product_photos
|
49
|
+
boolean_attribute :enable_invoice_logo
|
50
|
+
boolean_attribute :enable_frontend_auto_translate
|
51
|
+
|
52
|
+
object_attribute :logo_photo
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module CrystalApi
|
2
|
+
class Variant
|
3
|
+
include Attributes
|
4
|
+
|
5
|
+
root_element :variant
|
6
|
+
|
7
|
+
object_attribute :sell_price
|
8
|
+
object_attribute :buy_price
|
9
|
+
object_attribute :store_credit_buy_price
|
10
|
+
|
11
|
+
integer_attribute :id
|
12
|
+
integer_attribute :product_id
|
13
|
+
integer_attribute :product_catalog_id
|
14
|
+
integer_attribute :qty
|
15
|
+
integer_attribute :actual_qty
|
16
|
+
integer_attribute :inventory_qty
|
17
|
+
integer_attribute :wtb_qty
|
18
|
+
|
19
|
+
string_attribute :catalog_id
|
20
|
+
string_attribute :product_name
|
21
|
+
string_attribute :category_name
|
22
|
+
|
23
|
+
boolean_attribute :is_default
|
24
|
+
boolean_attribute :is_infinite_qty
|
25
|
+
|
26
|
+
array_attribute :descriptors, :object
|
27
|
+
end
|
28
|
+
end
|