etsy 0.1.0 → 0.2.0
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.
- data/README.rdoc +102 -19
- data/Rakefile +5 -5
- data/lib/etsy.rb +104 -26
- data/lib/etsy/basic_client.rb +26 -0
- data/lib/etsy/image.rb +19 -16
- data/lib/etsy/listing.rb +30 -27
- data/lib/etsy/model.rb +12 -49
- data/lib/etsy/request.rb +41 -18
- data/lib/etsy/response.rb +17 -11
- data/lib/etsy/secure_client.rb +79 -0
- data/lib/etsy/shop.rb +42 -18
- data/lib/etsy/user.rb +39 -34
- data/lib/etsy/verification_request.rb +17 -0
- data/lib/etsy/version.rb +4 -4
- data/test/fixtures/image/findAllListingImages.json +102 -0
- data/test/fixtures/listing/findAllShopListingsActive.json +69 -0
- data/test/fixtures/shop/findAllShop.json +1 -0
- data/test/fixtures/shop/findAllShop.single.json +1 -0
- data/test/fixtures/shop/getShop.multiple.json +1 -0
- data/test/fixtures/shop/getShop.single.json +32 -0
- data/test/fixtures/user/getUser.multiple.json +29 -0
- data/test/fixtures/user/getUser.single.json +13 -0
- data/test/fixtures/user/getUser.single.private.json +18 -0
- data/test/test_helper.rb +27 -42
- data/test/unit/etsy/basic_client_test.rb +28 -0
- data/test/unit/etsy/image_test.rb +36 -15
- data/test/unit/etsy/listing_test.rb +84 -66
- data/test/unit/etsy/request_test.rb +121 -39
- data/test/unit/etsy/response_test.rb +20 -20
- data/test/unit/etsy/secure_client_test.rb +110 -0
- data/test/unit/etsy/shop_test.rb +74 -40
- data/test/unit/etsy/user_test.rb +65 -61
- data/test/unit/etsy/verification_request_test.rb +26 -0
- data/test/unit/etsy_test.rb +91 -10
- metadata +61 -17
- data/test/fixtures/getShopDetails.json +0 -70
- data/test/fixtures/getShopListings.json +0 -135
- data/test/fixtures/getUserDetails.json +0 -34
data/lib/etsy/model.rb
CHANGED
@@ -1,64 +1,27 @@
|
|
1
1
|
module Etsy
|
2
2
|
module Model # :nodoc:all
|
3
|
-
|
3
|
+
|
4
4
|
module ClassMethods
|
5
|
-
|
6
|
-
def attribute(name, options = {})
|
7
|
-
from = options.fetch(:from, name)
|
8
5
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
CODE
|
6
|
+
def attribute(name, options = {})
|
7
|
+
define_method name do
|
8
|
+
@result[options.fetch(:from, name).to_s]
|
9
|
+
end
|
14
10
|
end
|
15
|
-
|
11
|
+
|
16
12
|
def attributes(*names)
|
17
13
|
names.each {|name| attribute(name) }
|
18
14
|
end
|
19
15
|
|
20
|
-
def finder(type, endpoint)
|
21
|
-
parameter = endpoint.scan(/:\w+/).first
|
22
|
-
parameter.sub!(/^:/, '')
|
23
|
-
|
24
|
-
endpoint.sub!(":#{parameter}", '#{' + parameter + '}')
|
25
|
-
|
26
|
-
send("find_#{type}", parameter, endpoint)
|
27
|
-
end
|
28
|
-
|
29
|
-
def find_all(parameter, endpoint)
|
30
|
-
class_eval <<-CODE
|
31
|
-
def self.find_all_by_#{parameter}(#{parameter})
|
32
|
-
response = Request.get("#{endpoint}")
|
33
|
-
response.result.map {|listing| new(listing) }
|
34
|
-
end
|
35
|
-
CODE
|
36
|
-
end
|
37
|
-
|
38
|
-
def find_one(parameter, endpoint)
|
39
|
-
class_eval <<-CODE
|
40
|
-
def self.find_by_#{parameter}(#{parameter})
|
41
|
-
response = Request.get("#{endpoint}")
|
42
|
-
new response.result
|
43
|
-
end
|
44
|
-
CODE
|
45
|
-
end
|
46
|
-
|
47
16
|
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
def initialize(result = nil)
|
52
|
-
@result = result
|
53
|
-
end
|
54
|
-
|
17
|
+
|
18
|
+
def initialize(result = nil)
|
19
|
+
@result = result
|
55
20
|
end
|
56
|
-
|
21
|
+
|
57
22
|
def self.included(other)
|
58
|
-
other.
|
59
|
-
other.send(:include, Etsy::Model::InstanceMethods)
|
23
|
+
other.extend ClassMethods
|
60
24
|
end
|
61
|
-
|
62
|
-
|
25
|
+
|
63
26
|
end
|
64
27
|
end
|
data/lib/etsy/request.rb
CHANGED
@@ -1,48 +1,71 @@
|
|
1
1
|
module Etsy
|
2
|
-
|
2
|
+
|
3
3
|
# = Request
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# A basic wrapper around GET requests to the Etsy JSON API
|
6
6
|
#
|
7
7
|
class Request
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
"http://beta-api.etsy.com/v1"
|
8
|
+
|
9
|
+
def self.host # :nodoc:
|
10
|
+
'openapi.etsy.com'
|
12
11
|
end
|
13
|
-
|
12
|
+
|
14
13
|
# Perform a GET request for the resource with optional parameters - returns
|
15
14
|
# A Response object with the payload data
|
16
15
|
def self.get(resource_path, parameters = {})
|
17
16
|
request = Request.new(resource_path, parameters)
|
18
17
|
Response.new(request.get)
|
19
18
|
end
|
20
|
-
|
19
|
+
|
21
20
|
# Create a new request for the resource with optional parameters
|
22
21
|
def initialize(resource_path, parameters = {})
|
23
22
|
@resource_path = resource_path
|
24
23
|
@parameters = parameters
|
25
24
|
end
|
26
25
|
|
26
|
+
def base_path # :nodoc:
|
27
|
+
parts = ['v2']
|
28
|
+
parts << 'sandbox' if Etsy.environment == :sandbox
|
29
|
+
parts << (secure? ? 'private' : 'public')
|
30
|
+
|
31
|
+
"/#{parts.join('/')}"
|
32
|
+
end
|
33
|
+
|
27
34
|
# Perform a GET request against the API endpoint and return the raw
|
28
35
|
# response data
|
29
36
|
def get
|
30
|
-
|
37
|
+
client.get(endpoint_url)
|
31
38
|
end
|
32
|
-
|
39
|
+
|
40
|
+
def client # :nodoc:
|
41
|
+
@client ||= secure? ? secure_client : basic_client
|
42
|
+
end
|
43
|
+
|
33
44
|
def parameters # :nodoc:
|
34
45
|
@parameters.merge(:api_key => Etsy.api_key, :detail_level => 'high')
|
35
46
|
end
|
36
|
-
|
47
|
+
|
37
48
|
def query # :nodoc:
|
38
49
|
parameters.map {|k,v| "#{k}=#{v}"}.join('&')
|
39
50
|
end
|
40
|
-
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
51
|
+
|
52
|
+
def endpoint_url # :nodoc:
|
53
|
+
"#{base_path}#{@resource_path}?#{query}"
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def secure_client
|
59
|
+
SecureClient.new(:access_token => @parameters[:access_token], :access_secret => @parameters[:access_secret])
|
60
|
+
end
|
61
|
+
|
62
|
+
def basic_client
|
63
|
+
BasicClient.new(self.class.host)
|
45
64
|
end
|
46
|
-
|
65
|
+
|
66
|
+
def secure?
|
67
|
+
Etsy.access_mode == :read_write && !@parameters[:access_token].nil? && !@parameters[:access_secret].nil?
|
68
|
+
end
|
69
|
+
|
47
70
|
end
|
48
|
-
end
|
71
|
+
end
|
data/lib/etsy/response.rb
CHANGED
@@ -1,30 +1,36 @@
|
|
1
1
|
module Etsy
|
2
|
-
|
2
|
+
|
3
3
|
# = Response
|
4
|
-
#
|
4
|
+
#
|
5
5
|
# Basic wrapper around the Etsy JSON response data
|
6
6
|
#
|
7
7
|
class Response
|
8
|
-
|
9
|
-
# Create a new response based on the raw
|
10
|
-
def initialize(
|
11
|
-
@
|
8
|
+
|
9
|
+
# Create a new response based on the raw HTTP response
|
10
|
+
def initialize(raw_response)
|
11
|
+
@raw_response = raw_response
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
# Convert the raw JSON data to a hash
|
15
15
|
def to_hash
|
16
|
-
@hash ||= JSON.parse(
|
16
|
+
@hash ||= JSON.parse(data)
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
# Number of records in the response results
|
20
20
|
def count
|
21
21
|
to_hash['count']
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
# Results of the API request
|
25
25
|
def result
|
26
26
|
count == 1 ? to_hash['results'].first : to_hash['results']
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def data
|
32
|
+
@raw_response.body
|
33
|
+
end
|
34
|
+
|
29
35
|
end
|
30
36
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Etsy
|
2
|
+
|
3
|
+
# = SecureClient
|
4
|
+
#
|
5
|
+
# Used for generating tokens and calling API methods that require authentication.
|
6
|
+
#
|
7
|
+
class SecureClient
|
8
|
+
|
9
|
+
# Create a new client with the necessary parameters. Accepts the following
|
10
|
+
# key/value pairs:
|
11
|
+
#
|
12
|
+
# :request_token
|
13
|
+
# :request_secret
|
14
|
+
# :access_token
|
15
|
+
# :access_secret
|
16
|
+
#
|
17
|
+
# The request token / secret is useful for generating the access token. Pass
|
18
|
+
# the access token / secret when initializing from stored credentials.
|
19
|
+
#
|
20
|
+
def initialize(attributes = {})
|
21
|
+
@attributes = attributes
|
22
|
+
end
|
23
|
+
|
24
|
+
def consumer # :nodoc:
|
25
|
+
@consumer ||= OAuth::Consumer.new(Etsy.api_key, Etsy.api_secret, {
|
26
|
+
:site => 'http://openapi.etsy.com',
|
27
|
+
:request_token_path => '/v2/sandbox/oauth/request_token',
|
28
|
+
:access_token_path => '/v2/sandbox/oauth/access_token',
|
29
|
+
:authorize_url => 'https://www.etsy.com/oauth/signin'
|
30
|
+
})
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generate a request token.
|
34
|
+
#
|
35
|
+
def request_token
|
36
|
+
consumer.get_request_token(:oauth_callback => Etsy.callback_url)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Access token for this request, either generated from the request token or taken
|
40
|
+
# from the :access_token parameter.
|
41
|
+
#
|
42
|
+
def access_token
|
43
|
+
@attributes[:access_token] || client.token
|
44
|
+
end
|
45
|
+
|
46
|
+
# Access secret for this request, either generated from the request token or taken
|
47
|
+
# from the :access_secret parameter.
|
48
|
+
#
|
49
|
+
def access_secret
|
50
|
+
@attributes[:access_secret] || client.secret
|
51
|
+
end
|
52
|
+
|
53
|
+
def client_from_request_data # :nodoc:
|
54
|
+
request_token = OAuth::RequestToken.new(consumer, @attributes[:request_token], @attributes[:request_secret])
|
55
|
+
request_token.get_access_token(:oauth_verifier => @attributes[:verifier])
|
56
|
+
end
|
57
|
+
|
58
|
+
def client_from_access_data # :nodoc:
|
59
|
+
OAuth::AccessToken.new(consumer, @attributes[:access_token], @attributes[:access_secret])
|
60
|
+
end
|
61
|
+
|
62
|
+
def client # :nodoc:
|
63
|
+
@client ||= has_access_data? ? client_from_access_data : client_from_request_data
|
64
|
+
end
|
65
|
+
|
66
|
+
# Fetch a raw response from the specified endpoint.
|
67
|
+
#
|
68
|
+
def get(endpoint)
|
69
|
+
client.get(endpoint)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def has_access_data?
|
75
|
+
!@attributes[:access_token].nil? && !@attributes[:access_secret].nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/lib/etsy/shop.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
module Etsy
|
2
|
-
|
2
|
+
|
3
3
|
# = Shop
|
4
4
|
#
|
5
|
-
# Represents a single Etsy shop. Users may or may not have an associated shop
|
6
|
-
# check the result of User#seller? to find out.
|
5
|
+
# Represents a single Etsy shop. Users may or may not have an associated shop.
|
7
6
|
#
|
8
7
|
# A shop has the following attributes:
|
9
8
|
#
|
@@ -11,40 +10,65 @@ module Etsy
|
|
11
10
|
# [title] A brief heading for the shop's main page
|
12
11
|
# [announcement] An announcement to buyers (displays on the shop's home page)
|
13
12
|
# [message] The message sent to users who buy from this shop
|
14
|
-
# [
|
15
|
-
# [
|
13
|
+
# [image_url] The full URL to the shops's banner image
|
14
|
+
# [active_listings_count] The number of active listings present in this shop
|
16
15
|
#
|
17
16
|
class Shop
|
18
|
-
|
17
|
+
|
19
18
|
include Etsy::Model
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
attributes :title, :announcement, :user_id
|
21
|
+
|
22
|
+
attribute :id, :from => :shop_id
|
23
|
+
attribute :image_url, :from => 'image_url_760x100'
|
24
|
+
attribute :active_listings_count, :from => 'listing_active_count'
|
25
|
+
attribute :updated, :from => :last_updated_tsz
|
26
|
+
attribute :created, :from => :creation_tsz
|
23
27
|
attribute :name, :from => :shop_name
|
24
|
-
attribute :updated, :from => :last_updated_epoch
|
25
|
-
attribute :created, :from => :creation_epoch
|
26
28
|
attribute :message, :from => :sale_message
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
+
# Retrieve one or more shops by name or ID:
|
31
|
+
#
|
32
|
+
# Etsy::Shop.find('reagent')
|
33
|
+
#
|
34
|
+
# You can find multiple shops by passing an array of identifiers:
|
35
|
+
#
|
36
|
+
# Etsy::Shop.find(['reagent', 'littletjane'])
|
37
|
+
#
|
38
|
+
def self.find(*identifiers)
|
39
|
+
response = Request.get("/shops/#{identifiers.join(',')}")
|
40
|
+
shops = [response.result].flatten.map {|data| new(data) }
|
41
|
+
|
42
|
+
(identifiers.length == 1) ? shops[0] : shops
|
43
|
+
end
|
44
|
+
|
45
|
+
# Retrieve a list of all shops. By default it fetches 25 at a time, but that can
|
46
|
+
# be configured by passing the :limit and :offset parameters:
|
47
|
+
#
|
48
|
+
# Etsy::Shop.all(:limit => 100, :offset => 100)
|
49
|
+
#
|
50
|
+
def self.all(options = {})
|
51
|
+
response = Request.get("/shops", options)
|
52
|
+
response.result.map {|data| new(data) }
|
53
|
+
end
|
54
|
+
|
30
55
|
# Time that this shop was created
|
31
56
|
#
|
32
57
|
def created_at
|
33
58
|
Time.at(created)
|
34
59
|
end
|
35
|
-
|
60
|
+
|
36
61
|
# Time that this shop was last updated
|
37
62
|
#
|
38
63
|
def updated_at
|
39
64
|
Time.at(updated)
|
40
65
|
end
|
41
|
-
|
42
|
-
#
|
43
|
-
# more information
|
66
|
+
|
67
|
+
# The collection of active listings associated with this shop
|
44
68
|
#
|
45
69
|
def listings
|
46
|
-
Listing.
|
70
|
+
@listings ||= Listing.find_all_by_shop_id(id)
|
47
71
|
end
|
48
|
-
|
72
|
+
|
49
73
|
end
|
50
74
|
end
|
data/lib/etsy/user.rb
CHANGED
@@ -1,54 +1,59 @@
|
|
1
1
|
module Etsy
|
2
|
-
|
2
|
+
|
3
3
|
# = User
|
4
4
|
#
|
5
5
|
# Represents a single Etsy user - has the following attributes:
|
6
6
|
#
|
7
7
|
# [id] The unique identifier for this user
|
8
8
|
# [username] This user's username
|
9
|
-
# [
|
10
|
-
# [city] The user's city / state (optional)
|
11
|
-
# [gender] The user's gender
|
12
|
-
# [bio] User's biography
|
9
|
+
# [email] This user's email address (authenticated calls only)
|
13
10
|
#
|
14
11
|
class User
|
15
|
-
|
12
|
+
|
16
13
|
include Etsy::Model
|
17
|
-
|
18
|
-
finder :one, '/users/:username'
|
19
|
-
|
20
|
-
attribute :username, :from => :user_name
|
14
|
+
|
21
15
|
attribute :id, :from => :user_id
|
22
|
-
attribute :
|
23
|
-
attribute :
|
24
|
-
attribute :
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# This user's shop, returns nil if user is not a seller. See Etsy::Shop
|
29
|
-
# for more information.
|
16
|
+
attribute :username, :from => :login_name
|
17
|
+
attribute :email, :from => :primary_email
|
18
|
+
attribute :created, :from => :creation_tsz
|
19
|
+
|
20
|
+
# Retrieve one or more users by name or ID:
|
30
21
|
#
|
31
|
-
|
32
|
-
|
22
|
+
# Etsy::User.find('reagent')
|
23
|
+
#
|
24
|
+
# You can find multiple users by passing an array of identifiers:
|
25
|
+
#
|
26
|
+
# Etsy::User.find(['reagent', 'littletjane'])
|
27
|
+
#
|
28
|
+
def self.find(*identifiers)
|
29
|
+
self.get("/users/#{identifiers.join(',')}")
|
33
30
|
end
|
34
|
-
|
35
|
-
#
|
31
|
+
|
32
|
+
# Retrieve the currently authenticated user.
|
36
33
|
#
|
37
|
-
def
|
38
|
-
|
34
|
+
def self.myself(token, secret)
|
35
|
+
self.get("/users/__SELF__", :access_token => token, :access_secret => secret)
|
39
36
|
end
|
40
|
-
|
41
|
-
#
|
37
|
+
|
38
|
+
# The shop associated with this user.
|
42
39
|
#
|
43
|
-
def
|
44
|
-
|
40
|
+
def shop
|
41
|
+
@shop ||= Shop.find(username)
|
45
42
|
end
|
46
|
-
|
47
|
-
# Time that this user
|
43
|
+
|
44
|
+
# Time that this user was created
|
48
45
|
#
|
49
|
-
def
|
50
|
-
Time.at(
|
46
|
+
def created_at
|
47
|
+
Time.at(created)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def self.get(query, options={})
|
53
|
+
response = Request.get(query, options)
|
54
|
+
users = [response.result].flatten.map {|data| new(data) }
|
55
|
+
|
56
|
+
(users.length == 1) ? users[0] : users
|
51
57
|
end
|
52
|
-
|
53
58
|
end
|
54
|
-
end
|
59
|
+
end
|