etsy 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/README.md +49 -6
- data/lib/etsy.rb +1 -0
- data/lib/etsy/image.rb +2 -2
- data/lib/etsy/listing.rb +61 -4
- data/lib/etsy/receipt.rb +3 -3
- data/lib/etsy/secure_client.rb +1 -1
- data/lib/etsy/variation/property_set.rb +71 -0
- data/lib/etsy/version.rb +1 -1
- data/test/fixtures/receipt/findAllShopReceipts.json +4 -4
- data/test/unit/etsy/image_test.rb +11 -3
- data/test/unit/etsy/listing_test.rb +31 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd7112229ce87257b5ea66ca6eddafe82d07bacc
|
4
|
+
data.tar.gz: e8f34ab7f54ee0675a26d387c77b337ca10ec2db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad057b670ae67786f6c4093218dfb615ede31d5c005b8e19c753f3a776fcb1b77e0cf3e1dbf5f7227ad303ab6320ae7d5c4d014be48684d2c8490279e1992a73
|
7
|
+
data.tar.gz: 50108ef28fcd663125db0787a1127d6a5ee8587f5b1c35cf7aee4776f6f503fb2995af90a595d8f1577b8a4e228c02c06adce626e45fa468629f49c395423f1c
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -26,6 +26,32 @@ It will likely work with higher versions, but this is unproven.
|
|
26
26
|
|
27
27
|
## Usage
|
28
28
|
|
29
|
+
In order to try this out you'll need to create an app on [etsy.com/your/apps](https://www.etsy.com/your/apps).
|
30
|
+
|
31
|
+
This will give you an api key and secret.
|
32
|
+
|
33
|
+
Paste the following into IRB, replacing the api key and secret with the ones you got on Etsy:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require 'etsy'
|
37
|
+
Etsy.protocol = "https"
|
38
|
+
Etsy.api_key = 'YOUR API KEY'
|
39
|
+
Etsy.api_secret = 'YOUR SECRET'
|
40
|
+
request = Etsy.request_token
|
41
|
+
Etsy.verification_url
|
42
|
+
```
|
43
|
+
|
44
|
+
Paste the verification URL into your browser, and authorize the application. That will give you a page
|
45
|
+
with a code on it. Paste the following into IRB, replacing the code:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
access = Etsy.access_token(request.token, request.secret, 'CODE')
|
49
|
+
Etsy.myself(access.token, access.secret)
|
50
|
+
```
|
51
|
+
|
52
|
+
If you've received a 401 unauthorized error, then you likely don't have a valid api key and secret, or
|
53
|
+
perhaps the verification url timed out.
|
54
|
+
|
29
55
|
### Public Mode
|
30
56
|
|
31
57
|
The Etsy API has two modes: public, and authenticated. Public mode only requires an
|
@@ -67,6 +93,11 @@ Authenticated calls can now be made by passing an access token and secret:
|
|
67
93
|
|
68
94
|
Etsy.myself(access.token, access.secret)
|
69
95
|
|
96
|
+
The key and secret have to be passed in for the authenticated calls.
|
97
|
+
|
98
|
+
auth = {:access_token=>access.token, :access_secret=>access.secret}
|
99
|
+
Etsy::Transaction.find_all_by_shop_id(shop_id, auth.merge(options))
|
100
|
+
|
70
101
|
### Web Application
|
71
102
|
|
72
103
|
The process for authenticating via a web application is similar, but requires the configuration of a callback URL:
|
@@ -278,16 +309,28 @@ I ask that you not submit patches that include changes to the version or gemspec
|
|
278
309
|
These people have helped make the Etsy gem what it is today:
|
279
310
|
|
280
311
|
* [Patrick Reagan](https://github.com/reagent)
|
281
|
-
* [Katrina Owen](
|
312
|
+
* [Katrina Owen](https://github.com/kytrinyx)
|
313
|
+
* [Trae Robrock](https://github.com/trobrock)
|
314
|
+
* [Roger Smith](https://github.com/rogsmith)
|
315
|
+
* [Jimmy Tang](https://github.com/jimmytang)
|
282
316
|
* [Mak Nazečić-Andrlon](https://github.com/Muon)
|
317
|
+
* [Daniel Szmulewicz](https://github.com/danielsz)
|
283
318
|
* [Patrick Schless](https://github.com/plainlystated)
|
319
|
+
* [Martin Popelak](https://github.com/pupca)
|
320
|
+
* [Julio Santos](https://github.com/julio)
|
284
321
|
* [Matt Fields](https://github.com/mfields106)
|
322
|
+
* [Mike Taylor](https://github.com/sealabcore)
|
323
|
+
* [Mason Stewart](https://github.com/masondesu)
|
324
|
+
* [Sasha Gerrand](https://github.com/sgerrand)
|
285
325
|
* [Jake Boxer](https://github.com/jakeboxer)
|
286
|
-
* [
|
287
|
-
* [
|
288
|
-
* [
|
289
|
-
* [
|
290
|
-
* [
|
326
|
+
* [Sunwoo Yang](https://github.com/sunwooz)
|
327
|
+
* [Isaac Rosenberg](https://github.com/irosenb)
|
328
|
+
* [Carson Gross](https://github.com/carsongross)
|
329
|
+
* [John Amicangelo](https://github.com/JohnAmican)
|
330
|
+
* [Max Gulyaev](https://github.com/maxilev)
|
331
|
+
* [fbehrens](https://github.com/fbehrens)
|
332
|
+
* [William Jeffries](https://github.com/williamcodes)
|
333
|
+
* [Jack Guoyuan Zhao](https://github.com/zhaoguoyuan)
|
291
334
|
|
292
335
|
### Github Flow
|
293
336
|
|
data/lib/etsy.rb
CHANGED
data/lib/etsy/image.rb
CHANGED
@@ -21,8 +21,8 @@ module Etsy
|
|
21
21
|
|
22
22
|
# Fetch all images for a given listing.
|
23
23
|
#
|
24
|
-
def self.find_all_by_listing_id(listing_id)
|
25
|
-
get_all("/listings/#{listing_id}/images")
|
24
|
+
def self.find_all_by_listing_id(listing_id, options = {})
|
25
|
+
get_all("/listings/#{listing_id}/images", options)
|
26
26
|
end
|
27
27
|
|
28
28
|
def self.create(listing, image_path, options = {})
|
data/lib/etsy/listing.rb
CHANGED
@@ -33,7 +33,7 @@ module Etsy
|
|
33
33
|
include Etsy::Model
|
34
34
|
|
35
35
|
STATES = %w(active removed sold_out expired alchemy)
|
36
|
-
VALID_STATES = [:active, :expired, :inactive, :sold, :featured]
|
36
|
+
VALID_STATES = [:active, :expired, :inactive, :sold, :featured, :draft, :sold_out]
|
37
37
|
|
38
38
|
attribute :id, :from => :listing_id
|
39
39
|
attribute :view_count, :from => :views
|
@@ -44,7 +44,8 @@ module Etsy
|
|
44
44
|
|
45
45
|
attributes :title, :description, :state, :url, :price, :quantity,
|
46
46
|
:tags, :materials, :hue, :saturation, :brightness, :is_black_and_white,
|
47
|
-
:featured_rank, :occasion, :num_favorers, :user_id
|
47
|
+
:featured_rank, :occasion, :num_favorers, :user_id,
|
48
|
+
:shipping_template_id, :who_made, :when_made
|
48
49
|
|
49
50
|
association :image, :from => 'Images'
|
50
51
|
|
@@ -79,7 +80,7 @@ module Etsy
|
|
79
80
|
# By default, pulls back the first 25 active listings.
|
80
81
|
# Defaults can be overridden using :limit, :offset, and :state
|
81
82
|
#
|
82
|
-
# Available states are :active, :expired, :inactive, :sold, and :featured
|
83
|
+
# Available states are :active, :expired, :inactive, :sold, and :featured, :draft, :sold_out
|
83
84
|
# where :featured is a subset of the others.
|
84
85
|
#
|
85
86
|
# options = {
|
@@ -123,7 +124,7 @@ module Etsy
|
|
123
124
|
# The collection of images associated with this listing.
|
124
125
|
#
|
125
126
|
def images
|
126
|
-
@images ||= Image.find_all_by_listing_id(id)
|
127
|
+
@images ||= Image.find_all_by_listing_id(id, oauth)
|
127
128
|
end
|
128
129
|
|
129
130
|
# The primary image for this listing.
|
@@ -132,6 +133,53 @@ module Etsy
|
|
132
133
|
images.first
|
133
134
|
end
|
134
135
|
|
136
|
+
def variations
|
137
|
+
self.class.get_all("/listings/#{id}/variations")
|
138
|
+
end
|
139
|
+
|
140
|
+
# If these are your desired variations:
|
141
|
+
# - Dimensions: 1 x 2 inches
|
142
|
+
# - Dimensions: 2 x 4 inches
|
143
|
+
#
|
144
|
+
# Then you first have to find the property ID of the property you want to vary. Eg:
|
145
|
+
# Etsy::Variation::PropertySet.find_property_by_name("Dimensions").fetch("property_id")
|
146
|
+
#
|
147
|
+
# Then you can decide which options you want to set for this property.
|
148
|
+
# Eg:
|
149
|
+
# Etsy::Variation::PropertySet.qualifying_properties_for_property("Dimension")
|
150
|
+
# => [{
|
151
|
+
# "param"=>"dimensions_scale",
|
152
|
+
# "description"=>"Sizing Scale",
|
153
|
+
# "options"=>{
|
154
|
+
# "Inches" => 344,
|
155
|
+
# "Centimeters" => 345,
|
156
|
+
# "Other" => 346
|
157
|
+
# }
|
158
|
+
# }]
|
159
|
+
#
|
160
|
+
# Put it all together for a call to add_variations:
|
161
|
+
|
162
|
+
# property_id = Etsy::Variation::PropertySet.find_property_by_name("Dimensions").fetch("property_id")
|
163
|
+
# scale = Etsy::Variation::PropertySet.qualifying_properties_for_property("Dimensions").detect {|qp| qp.fetch("description") == "Sizing Scale"}
|
164
|
+
# my_listing.add_variations(
|
165
|
+
# :variations => [
|
166
|
+
# {"property_id" => property_id, "value" => "1 x 2", "is_available" => true, "price" => 1.23},
|
167
|
+
# {"property_id" => property_id, "value" => "2 x 4", "is_available" => true, "price" => 2.34}
|
168
|
+
# ],
|
169
|
+
# scale.fetch("param") => scale.fetch("options").fetch("Inches")
|
170
|
+
# )
|
171
|
+
def add_variations(options)
|
172
|
+
options[:variations] = JSON.dump(options.delete(:variations))
|
173
|
+
options[:require_secure] = true
|
174
|
+
self.class.post("/listings/#{id}/variations", options)
|
175
|
+
end
|
176
|
+
|
177
|
+
def update_variations(options)
|
178
|
+
options[:variations] = JSON.dump(options.delete(:variations))
|
179
|
+
options[:require_secure] = true
|
180
|
+
self.class.put("/listings/#{id}/variations", options)
|
181
|
+
end
|
182
|
+
|
135
183
|
def black_and_white?
|
136
184
|
is_black_and_white
|
137
185
|
end
|
@@ -169,6 +217,10 @@ module Etsy
|
|
169
217
|
(user_ids.size > 0) ? Array(Etsy::User.find(user_ids, options)) : []
|
170
218
|
end
|
171
219
|
|
220
|
+
def is_supply
|
221
|
+
!!@result.fetch("is_supply")
|
222
|
+
end
|
223
|
+
|
172
224
|
private
|
173
225
|
|
174
226
|
def self.valid?(state)
|
@@ -209,5 +261,10 @@ module Etsy
|
|
209
261
|
(listing_ids.size > 0) ? Array(find(listing_ids, options)) : []
|
210
262
|
end
|
211
263
|
|
264
|
+
private
|
265
|
+
|
266
|
+
def oauth
|
267
|
+
oauth = (token && secret) ? {:access_token => token, :access_secret => secret} : {}
|
268
|
+
end
|
212
269
|
end
|
213
270
|
end
|
data/lib/etsy/receipt.rb
CHANGED
@@ -6,14 +6,14 @@ module Etsy
|
|
6
6
|
attribute :buyer_id, :from => :buyer_user_id
|
7
7
|
|
8
8
|
attributes :quantity, :listing_id, :name, :first_line, :second_line, :city, :state, :zip, :country_id,
|
9
|
-
:payment_email, :buyer_email, :
|
9
|
+
:payment_email, :buyer_email, :creation_tsz
|
10
10
|
|
11
11
|
def self.find_all_by_shop_id(shop_id, options = {})
|
12
12
|
get_all("/shops/#{shop_id}/receipts", options)
|
13
13
|
end
|
14
14
|
|
15
15
|
def created_at
|
16
|
-
Time.at(
|
16
|
+
Time.at(creation_tsz)
|
17
17
|
end
|
18
18
|
|
19
19
|
def buyer
|
@@ -21,4 +21,4 @@ module Etsy
|
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
24
|
-
end
|
24
|
+
end
|
data/lib/etsy/secure_client.rb
CHANGED
@@ -107,7 +107,7 @@ module Etsy
|
|
107
107
|
if value.respond_to?(:read)
|
108
108
|
body << "Content-Disposition: form-data; name=\"#{esc_key}\"; filename=\"#{File.basename(value.path)}\"#{crlf}"
|
109
109
|
body << "Content-Type: image/jpeg#{crlf*2}"
|
110
|
-
body << value.read
|
110
|
+
body << open(value.path, "rb") {|io| io.read}
|
111
111
|
else
|
112
112
|
body << "Content-Disposition: form-data; name=\"#{esc_key}\"#{crlf*2}#{value}"
|
113
113
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Etsy
|
2
|
+
module Variation
|
3
|
+
class PropertySet
|
4
|
+
# Used to find properties which the seller can vary (eg Size, Color, etc), and options associated with those properties (eg Inches)
|
5
|
+
#
|
6
|
+
# The variations stuff is complex. See https://www.etsy.com/developers/documentation/getting_started/variations for more info.
|
7
|
+
#
|
8
|
+
# Probably the most useful method here is qualifying_properties_for_property. This method pulls in data from a few sources to get
|
9
|
+
# you everything you need to set up your variation options.
|
10
|
+
#
|
11
|
+
# Eg: I want to have a few Length options on my listings, and I want to use the unit Inches, then:
|
12
|
+
# Etsy::Variation::PropertySet.qualifying_properties_for_property("Length")
|
13
|
+
# => [{
|
14
|
+
# "param"=>"sizing_scale",
|
15
|
+
# "description"=>"Sizing Scale",
|
16
|
+
# "options"=>{
|
17
|
+
# "Alpha" => 301,
|
18
|
+
# "Inches" => 327,
|
19
|
+
# "Centimeters" => 328,
|
20
|
+
# "Fluid Ounces" => 335,
|
21
|
+
# "Millilitres" => 336,
|
22
|
+
# "Litres" => 337,
|
23
|
+
# "Other => 329
|
24
|
+
# }
|
25
|
+
# }]
|
26
|
+
#
|
27
|
+
# This tells me I want to set the parameter sizing_scale to 327 when calling Etsy::Listing.add_variations.
|
28
|
+
|
29
|
+
include Etsy::Model
|
30
|
+
|
31
|
+
|
32
|
+
attributes :properties, :category_id, :options, :qualifiers,
|
33
|
+
:qualifying_properties
|
34
|
+
|
35
|
+
def self.all
|
36
|
+
@all ||= get("/property_sets")
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.find_property_by_name(name)
|
40
|
+
property = all.properties.detect {|prop_id, prop| prop["name"] == name}
|
41
|
+
if property
|
42
|
+
property_id, property_data = property
|
43
|
+
property_data
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.qualifying_properties_for_property(name)
|
48
|
+
property = find_property_by_name(name)
|
49
|
+
return nil unless property
|
50
|
+
property_id = property["property_id"]
|
51
|
+
|
52
|
+
qualifiers = all.qualifiers[property_id.to_s]
|
53
|
+
return [] unless qualifiers
|
54
|
+
|
55
|
+
qualifiers.map do |qualifier|
|
56
|
+
qualifying_properties = all.qualifying_properties[qualifier.fetch("property_id").to_s]
|
57
|
+
options = qualifier.fetch("options").inject({}) do |acc, opt_id|
|
58
|
+
acc.merge({
|
59
|
+
all.options.fetch(opt_id.to_s) => opt_id
|
60
|
+
})
|
61
|
+
end
|
62
|
+
{
|
63
|
+
"param" => qualifying_properties.fetch("param"),
|
64
|
+
"description" => qualifying_properties.fetch("description"),
|
65
|
+
"options" => options
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/etsy/version.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
{
|
1
|
+
{
|
2
2
|
"count":1,
|
3
3
|
"results":
|
4
4
|
[
|
5
5
|
{
|
6
6
|
"receipt_id":27230877,
|
7
7
|
"buyer_user_id":12345,
|
8
|
-
"
|
8
|
+
"creation_tsz":1412206858,
|
9
9
|
"quantity":5,
|
10
10
|
"listing_id":123456,
|
11
11
|
"name":"Mike Taylor",
|
@@ -19,10 +19,10 @@
|
|
19
19
|
"buyer_email":"bar@example.com"
|
20
20
|
}
|
21
21
|
],
|
22
|
-
"params": {
|
22
|
+
"params": {
|
23
23
|
"shop_id":"5818087",
|
24
24
|
"limit":25,
|
25
25
|
"offset":0
|
26
26
|
},
|
27
27
|
"type":"Receipt"
|
28
|
-
}
|
28
|
+
}
|
@@ -5,11 +5,19 @@ module Etsy
|
|
5
5
|
|
6
6
|
context "The Image class" do
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
context "without oauth" do
|
9
|
+
should "be able to find all images for a listing" do
|
10
|
+
images = mock_request('/listings/1/images', {}, 'Image', 'findAllListingImages.json')
|
11
|
+
Image.find_all_by_listing_id(1, {}).should == images
|
12
|
+
end
|
11
13
|
end
|
12
14
|
|
15
|
+
context "with options" do
|
16
|
+
should "be able to find all images for a listing with options in request" do
|
17
|
+
images = mock_request('/listings/1/images', {foo: "bar"}, 'Image', 'findAllListingImages.json')
|
18
|
+
Image.find_all_by_listing_id(1, {foo: "bar"}).should == images
|
19
|
+
end
|
20
|
+
end
|
13
21
|
end
|
14
22
|
|
15
23
|
context "An instance of the Image class" do
|
@@ -32,6 +32,16 @@ module Etsy
|
|
32
32
|
Listing.find_all_by_shop_id(1, :state => :inactive).should == listings
|
33
33
|
end
|
34
34
|
|
35
|
+
should "be able to find draft listings" do
|
36
|
+
listings = mock_request('/shops/1/listings/draft', {}, 'Listing', 'findAllShopListings.json')
|
37
|
+
Listing.find_all_by_shop_id(1, :state => :draft).should == listings
|
38
|
+
end
|
39
|
+
|
40
|
+
should "be able to find sold_out listings" do
|
41
|
+
listings = mock_request('/shops/1/listings/sold_out', {}, 'Listing', 'findAllShopListings.json')
|
42
|
+
Listing.find_all_by_shop_id(1, :state => :sold_out).should == listings
|
43
|
+
end
|
44
|
+
|
35
45
|
should "be able to find featured listings" do
|
36
46
|
listings = mock_request('/shops/1/listings/featured', {}, 'Listing', 'findAllShopListings.json')
|
37
47
|
Listing.find_all_by_shop_id(1, :state => :featured).should == listings
|
@@ -196,15 +206,31 @@ module Etsy
|
|
196
206
|
end
|
197
207
|
end
|
198
208
|
|
199
|
-
|
200
|
-
|
201
|
-
|
209
|
+
context "with oauth" do
|
210
|
+
should "have a collection of images" do
|
211
|
+
listing = Listing.new
|
212
|
+
listing.stubs(:id).with().returns(1)
|
213
|
+
listing.stubs(:token).with().returns("token")
|
214
|
+
listing.stubs(:secret).with().returns("secret")
|
215
|
+
|
216
|
+
Image.stubs(:find_all_by_listing_id).with(1, {access_token: "token", access_secret: "secret"}).returns('images')
|
202
217
|
|
203
|
-
|
218
|
+
listing.images.should == 'images'
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context "without oauth" do
|
223
|
+
should "have a collection of images" do
|
224
|
+
listing = Listing.new
|
225
|
+
listing.stubs(:id).with().returns(1)
|
204
226
|
|
205
|
-
|
227
|
+
Image.stubs(:find_all_by_listing_id).with(1, {}).returns('images')
|
228
|
+
|
229
|
+
listing.images.should == 'images'
|
230
|
+
end
|
206
231
|
end
|
207
232
|
|
233
|
+
|
208
234
|
should "have a default image" do
|
209
235
|
listing = Listing.new
|
210
236
|
listing.stubs(:images).with().returns(%w(image_1 image_2))
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: etsy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Patrick Reagan
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-09-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -144,6 +144,7 @@ files:
|
|
144
144
|
- lib/etsy/shop.rb
|
145
145
|
- lib/etsy/transaction.rb
|
146
146
|
- lib/etsy/user.rb
|
147
|
+
- lib/etsy/variation/property_set.rb
|
147
148
|
- lib/etsy/verification_request.rb
|
148
149
|
- lib/etsy/version.rb
|
149
150
|
- test/fixtures/address/getUserAddresses.json
|