discogs-wrapper 1.1.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/LICENSE +9 -0
  2. data/README.markdown +123 -43
  3. data/lib/discogs-wrapper.rb +1 -0
  4. data/lib/discogs.rb +1 -0
  5. data/lib/wrapper/authentication.rb +56 -0
  6. data/lib/wrapper/wrapper.rb +732 -43
  7. data/spec/samples/valid_artist.json +1 -0
  8. data/spec/samples/valid_artist_releases.json +1 -0
  9. data/spec/samples/valid_fields.json +48 -0
  10. data/spec/samples/valid_folder.json +6 -0
  11. data/spec/samples/valid_identity.json +6 -0
  12. data/spec/samples/valid_label.json +1 -0
  13. data/spec/samples/valid_label_releases.json +1 -0
  14. data/spec/samples/valid_listing.json +1 -0
  15. data/spec/samples/valid_master_release.json +1 -0
  16. data/spec/samples/valid_master_release_versions.json +1 -0
  17. data/spec/samples/valid_order.json +57 -0
  18. data/spec/samples/valid_order_messages.json +102 -0
  19. data/spec/samples/valid_orders.json +68 -0
  20. data/spec/samples/valid_price_suggestions.json +34 -0
  21. data/spec/samples/valid_release.json +1 -0
  22. data/spec/samples/valid_search_results.json +1 -0
  23. data/spec/samples/valid_user.json +1 -0
  24. data/spec/samples/valid_user_collection.json +1 -0
  25. data/spec/samples/valid_user_folder.json +6 -0
  26. data/spec/samples/valid_user_folders.json +16 -0
  27. data/spec/samples/valid_user_inventory.json +41 -0
  28. data/spec/samples/valid_user_profile.json +25 -0
  29. data/spec/samples/valid_user_wantlist.json +1 -0
  30. data/spec/samples/valid_wantlist_release.json +41 -0
  31. data/spec/spec_helper.rb +4 -18
  32. data/spec/wrapper_methods/add_release_to_user_wantlist_spec.rb +42 -0
  33. data/spec/wrapper_methods/edit_release_in_user_wantlist_spec.rb +38 -0
  34. data/spec/wrapper_methods/edit_user_spec.rb +36 -0
  35. data/spec/wrapper_methods/get_artist_releases_spec.rb +49 -0
  36. data/spec/wrapper_methods/get_artist_spec.rb +15 -75
  37. data/spec/wrapper_methods/get_identity_spec.rb +35 -0
  38. data/spec/wrapper_methods/get_label_releases_spec.rb +49 -0
  39. data/spec/wrapper_methods/get_label_spec.rb +15 -28
  40. data/spec/wrapper_methods/get_listing_spec.rb +40 -0
  41. data/spec/wrapper_methods/get_master_release_spec.rb +16 -42
  42. data/spec/wrapper_methods/get_master_release_versions_spec.rb +49 -0
  43. data/spec/wrapper_methods/get_order_messages_spec.rb +36 -0
  44. data/spec/wrapper_methods/get_order_spec.rb +36 -0
  45. data/spec/wrapper_methods/get_price_suggestions_spec.rb +36 -0
  46. data/spec/wrapper_methods/get_release_spec.rb +26 -52
  47. data/spec/wrapper_methods/get_user_collection_spec.rb +41 -0
  48. data/spec/wrapper_methods/get_user_folder_spec.rb +46 -0
  49. data/spec/wrapper_methods/get_user_folders_spec.rb +42 -0
  50. data/spec/wrapper_methods/get_user_inventory_spec.rb +45 -0
  51. data/spec/wrapper_methods/get_user_spec.rb +45 -0
  52. data/spec/wrapper_methods/get_user_wantlist_spec.rb +49 -0
  53. data/spec/wrapper_methods/search_spec.rb +22 -293
  54. data/spec/wrapper_spec.rb +124 -45
  55. metadata +162 -67
  56. data/Rakefile +0 -26
  57. data/discogs.gemspec +0 -22
  58. data/lib/wrapper/resource.rb +0 -80
  59. data/lib/wrapper/resource_mappings.rb +0 -80
  60. data/lib/wrapper/resources/artist.rb +0 -40
  61. data/lib/wrapper/resources/artist_release.rb +0 -28
  62. data/lib/wrapper/resources/format.rb +0 -11
  63. data/lib/wrapper/resources/generic_list.rb +0 -29
  64. data/lib/wrapper/resources/image.rb +0 -11
  65. data/lib/wrapper/resources/label.rb +0 -16
  66. data/lib/wrapper/resources/label_release.rb +0 -17
  67. data/lib/wrapper/resources/master_release.rb +0 -19
  68. data/lib/wrapper/resources/master_release_version.rb +0 -17
  69. data/lib/wrapper/resources/release.rb +0 -25
  70. data/lib/wrapper/resources/release_artist.rb +0 -21
  71. data/lib/wrapper/resources/release_label.rb +0 -10
  72. data/lib/wrapper/resources/search.rb +0 -61
  73. data/lib/wrapper/resources/search_result.rb +0 -17
  74. data/lib/wrapper/resources/track.rb +0 -19
  75. data/lib/wrapper/resources/user.rb +0 -11
  76. data/lib/wrapper/resources/video.rb +0 -11
  77. data/spec/resource_spec.rb +0 -27
  78. data/spec/resources/artist_release_spec.rb +0 -59
  79. data/spec/resources/artist_spec.rb +0 -15
  80. data/spec/resources/format_spec.rb +0 -41
  81. data/spec/resources/generic_list_spec.rb +0 -66
  82. data/spec/resources/image_spec.rb +0 -43
  83. data/spec/resources/label_release_spec.rb +0 -55
  84. data/spec/resources/label_spec.rb +0 -15
  85. data/spec/resources/master_release_spec.rb +0 -15
  86. data/spec/resources/master_release_version_spec.rb +0 -55
  87. data/spec/resources/release_artist_spec.rb +0 -43
  88. data/spec/resources/release_label_spec.rb +0 -31
  89. data/spec/resources/release_spec.rb +0 -15
  90. data/spec/resources/search_result_spec.rb +0 -47
  91. data/spec/resources/search_spec.rb +0 -15
  92. data/spec/resources/track_spec.rb +0 -56
  93. data/spec/resources/video_spec.rb +0 -43
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ Copyright (c) 2010 Andrew Buntine
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
+
9
+ Based on a work at www.discogs.com.
data/README.markdown CHANGED
@@ -1,11 +1,17 @@
1
1
  Discogs::Wrapper
2
2
  ================
3
3
 
4
+ NOTE
5
+ ----
6
+ We are currently working on the next release of the API. It will implement all endpoints, pagination, have oAuth support, and use JSON exclusively.
7
+
8
+ Expecting to finish late-May, 2014.
9
+
4
10
  ABOUT
5
11
  -----
6
- A 100% Ruby wrapper of the Discogs.com API. No dependencies, no extra gems. :)
12
+ A 100% Ruby wrapper of the Discogs.com API.
7
13
 
8
- Discogs::Wrapper abstracts all the nasty boilerplate code needed to interact with the Discogs API. It gives you direct access to the information you need.
14
+ Discogs::Wrapper abstracts all of the boilerplate code needed to interact with the Discogs API. It gives you direct access to the information you need. All methods return a ruby Hash wrapped in a [Hashie](https://github.com/intridea/hashie) object with the same structure as documented on the [Discogs API website](http://www.discogs.com/developers/index.html).
9
15
 
10
16
  The master branch aims to give full support for version 2.0 of the API. If you need support for everything in version 1.0, see the api-v1 branch.
11
17
 
@@ -13,66 +19,140 @@ ABOUT
13
19
 
14
20
  * Artists
15
21
  * Releases
16
- * MasterReleases
22
+ * Master Releases
17
23
  * Labels
24
+ * Images
18
25
  * Searching (all of the above)
26
+ * Marketplace
27
+ * User Inventories
28
+ * Orders
29
+ * Fee Calculations
30
+ * Price Suggestions
31
+ * User Profiles
32
+ * Collections
33
+ * Wantlists
34
+ * oAuth
35
+ * Pagination / Sorting
19
36
 
20
- Please, [see the Wiki](http://github.com/buntine/discogs/wiki) for helpful documentation.
21
37
 
22
- The Discogs API is [documented here](http://www.discogs.com/help/api).
38
+ The Discogs API is [documented here](http://www.discogs.com/developers/index.html).
39
+
40
+ You can see all implemented methods on [this projects RDoc page](http://rdoc.info/github/buntine/discogs/master/frames).
23
41
 
24
42
  INSTALLATION
25
43
  ------------
26
44
  You can install the library via Rubygems:
27
45
 
28
- $ sudo gem install discogs-wrapper
46
+ $ gem install discogs-wrapper
47
+
48
+ Or within your Gemfile:
49
+
50
+ gem "discogs-wrapper"
29
51
 
30
52
  USAGE
31
53
  -----
32
54
  To use this library, you must supply the name of your application. For example:
33
55
 
34
- require 'discogs'
35
56
  wrapper = Discogs::Wrapper.new("My awesome web app")
36
57
 
37
58
  Accessing information is easy:
38
59
 
39
- artist = wrapper.get_artist("Master's Hammer")
40
- release = wrapper.get_release("611973") # Supply an ID.
41
- label = wrapper.get_label("Monitor Records")
42
- search = wrapper.search("Necrovore")
43
-
44
- artist.name # => "Master's Hammer"
45
- artist.releases[0].title # => "Finished"
46
- artist.releases[1].year # => "1989"
47
- artist.releases[4].extraartists # => [ "Arakain", "Debustrol" ]
48
-
49
- release.title # => "Ritual"
50
- release.labels[0].name # => "Osmose Productions"
51
- release.formats[0].descriptions[0] # => "LP"
52
- release.styles # => [ "Black Metal", "Death Metal" ]
53
- release.tracklist[1].title # => "Pad modly"
54
-
55
- label.images[0].width # => "220"
56
- label.releases.length # => 22
57
- label.releases[3].artist # => "Root"
58
- label.releases[7].catno # => "MON007"
59
-
60
- search.total_results # => 124
61
- search.total_pages # => 7
62
- search.current_page # => 1
63
-
64
- # Exact results
65
- search.exact[0].type # => "artist"
66
- search.exact[0].title # => "Necrovore"
67
- search.exact(:label)[0].title # => "Necrovores Records"
68
- search.closest(:artist) # => <Discogs::Search::Result:0x324ad3e2>
69
-
70
- # All results
71
- search.results[3].title # => "Necrovore - Demo '87"
72
- search.results[3].summary # => "First and only demo tape"
73
- search.results(:release)[0] # => <Discogs::Search::Result:0x343de34a>
60
+ artist = wrapper.get_artist("329937")
61
+ artist_releases = wrapper.get_artist_releases("329937")
62
+ release = wrapper.get_release("1529724")
63
+ label = wrapper.get_label("29515")
64
+ search = wrapper.search("Necrovore", :per_page => 10, :type => :artist)
65
+
66
+ artist.name # => "Manilla Road"
67
+ artist.members.count # => 4
68
+ artist.members.first.name # => "Mark Shelton"
69
+ artist.profile # => "Heavy Metal band from ..."
70
+
71
+ artist_releases.releases.count # => 35
72
+ artist_releases.releases.first.title # => "Invasion"
73
+
74
+ release.title # => "Medieval"
75
+ release.labels.first.name # => "New Renaissance Records"
76
+ release.formats[0].descriptions[0] # => "12\""
77
+ release.styles # => [ "Heavy Metal", "Doom Metal" ]
78
+ release.tracklist[1].title # => "Death is Beauty"
79
+
80
+ label.name # => "Monitor (2)"
81
+ label.sublabels.count # => 3
82
+
83
+ search.pagination.items # => 2
84
+ search.results.first.title # => "Necrovore"
85
+ search.results.first.type # => "artist"
86
+ search.results.first.id # => 691078
87
+
88
+ Many of the API endpoints return further URLs that will yield specific data. To cater for this, the library provides a "raw" method that accepts a valid API URL. For example:
89
+
90
+ sts_records = wrapper.get_label(9800)
91
+ sts_releases = wrapper.raw(sts_records.releases_url)
92
+ first_sts_release = wrapper.raw(sts_releases.releases[1].resource_url)
93
+
94
+ first_sts_release.title # => "I'll Nostra Tempo De La Vita / Having The Time Of Your Life"
95
+
96
+ You can see all implemented methods on [this projects RDoc page](http://rdoc.info/github/buntine/discogs/master/frames).
97
+
98
+ AUTHENTICATION
99
+ --------------
100
+ Many of the API endpoints require the user to be authenticated via oAuth. The library provides support for this.
101
+
102
+ I've provided [https://github.com/buntine/discogs-oauth](a simple Rails application) that demonstrates how to perform authenticated requests.
103
+
104
+ Make sure you've created an "app" in your developer settings on the Discogs website. You will need your consumer key and consumer secret.
105
+
106
+ Basically, you should preform the "oAuth dance" like so:
107
+
108
+ # Add an action to initiate the process.
109
+ def authenticate
110
+ @discogs = Discogs::Wrapper.new("Test OAuth")
111
+ request_data = @discogs.get_request_token("YOUR_APP_KEY", "YOUR_APP_SECRET", "http://127.0.0.1:3000/callback")
112
+
113
+ session[:request_token] = request_data[:request_token]
114
+
115
+ redirect_to request_data[:authorize_url]
116
+ end
117
+
118
+ # And an action that Discogs will redirect back to.
119
+ def callback
120
+ @discogs = Discogs::Wrapper.new("Test OAuth")
121
+ request_token = session[:request_token]
122
+ verifier = params[:oauth_verifier]
123
+ access_token = @discogs.authenticate(request_token, verifier)
124
+
125
+ session[:request_token] = nil
126
+ session[:access_token] = access_token
127
+
128
+ @discogs.access_token = access_token
129
+
130
+ # You can now perform authenticated requests.
131
+ end
132
+
133
+ # Once you have it, you can also pass your access_token into the constructor.
134
+ def another_action
135
+ @discogs = Discogs::Wrapper.new("Test OAuth", session[:access_token])
136
+
137
+ # You can now perform authenticated requests.
138
+ end
139
+
140
+ PAGINATION
141
+ ----------
142
+ All API endpoints that accept pagination, sorting or other parameters are supported.
143
+
144
+ Page defaults to 1, page size defaults to 50.
145
+
146
+ wrapper.get_artist_releases(345211, :page => 2, :per_page => 10)
147
+
148
+ If other params are accepted, they can also be passed:
149
+
150
+ wrapper.get_user_inventory("username", :page => 3, :sort => "price", :sort_order => "asc")
74
151
 
75
152
  LICENSE
76
153
  -----
154
+ See the LICENCE file. Copyright (c) Andrew Buntine
77
155
 
78
- See the LICENCE file. Copyright (c) Andrew Buntine
156
+ CONTRIBUTORS
157
+ ------------
158
+ List all contributors.
@@ -0,0 +1 @@
1
+ require 'discogs'
data/lib/discogs.rb CHANGED
@@ -15,6 +15,7 @@ module Discogs; end
15
15
  # Custom exceptions.
16
16
  class Discogs::UnknownResource < Exception; end
17
17
  class Discogs::InternalServerError < Exception; end
18
+ class Discogs::AuthenticationError < Exception; end
18
19
 
19
20
  # Loading sequence.
20
21
  require File.dirname(__FILE__) + "/wrapper/wrapper"
@@ -0,0 +1,56 @@
1
+ require 'oauth'
2
+
3
+ module Authentication
4
+
5
+ # Retrieves an OAuth request token from the Discogs server.
6
+ # @!macro [new] app_key
7
+ # @!macro [new] app_secret
8
+ # @param app_key [String] application id
9
+ # @param app_secret [String] application secret
10
+ # @return [Hash] containing a :request_token that should be stored locally and a :authorize_url that the user must browse to.
11
+ def get_request_token(app_key, app_secret, callback)
12
+ consumer = OAuth::Consumer.new(app_key, app_secret,
13
+ :authorize_url => "http://www.discogs.com/oauth/authorize",
14
+ :site => "http://api.discogs.com")
15
+ request_token = consumer.get_request_token(:oauth_callback => callback)
16
+
17
+ {:request_token => request_token,
18
+ :authorize_url => request_token.authorize_url(:oauth_callback => callback)}
19
+ end
20
+
21
+ # Retrieves an OAuth access token from the Discogs server.
22
+ # @!macro [new] request_token
23
+ # @!macro [new] verifier
24
+ # @param request_token [OAuth::RequestToken] request token
25
+ # @param verifier [String] verifier token
26
+ # @return [OAuth::AccessToken]
27
+ def authenticate(request_token, verifier)
28
+ @access_token = request_token.get_access_token(:oauth_verifier => verifier)
29
+ end
30
+
31
+ # Returns true if an OAuth access_token is present. If a username is given, the authenticated username must match it.
32
+ # @!macro [new] username
33
+ # @param username [String] username
34
+ # @return [Boolean]
35
+ def authenticated?(username=nil, &block)
36
+ auth = if username
37
+ @access_token and authenticated_username == username
38
+ else
39
+ !!@access_token
40
+ end
41
+
42
+ if block_given?
43
+ auth ? yield : raise_authentication_error
44
+ else
45
+ auth
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def authenticated_username
52
+ data = get_identity
53
+ data.username
54
+ end
55
+
56
+ end
@@ -1,114 +1,803 @@
1
1
  # Core API wrapper class.
2
2
 
3
- require 'uri'
3
+ require 'hashie'
4
+ require 'json'
4
5
  require 'net/http'
5
- require 'rexml/document'
6
- require 'zlib'
7
6
  require 'stringio'
7
+ require 'uri'
8
+ require 'cgi'
9
+ require 'zlib'
8
10
 
9
- require File.dirname(__FILE__) + "/resource"
11
+ require File.dirname(__FILE__) + "/authentication"
10
12
 
11
13
  class Discogs::Wrapper
12
14
 
15
+ include Authentication
16
+
13
17
  @@root_host = "http://api.discogs.com"
14
18
 
15
19
  attr_reader :app_name
20
+ attr_accessor :access_token
21
+
22
+ def initialize(app_name, access_token=nil)
23
+ @app_name = app_name
24
+ @access_token = access_token
25
+ end
16
26
 
17
- def initialize(app_name=nil)
18
- @app_name = app_name
27
+ # Retrieves a release by ID.
28
+ # @!macro [new] release_id
29
+ # @param release_id [Integer] release id
30
+ # @return [Hash] the release with provided release_id
31
+ def get_release(release_id)
32
+ query_and_build "releases/#{release_id}"
19
33
  end
20
34
 
21
- def get_release(id)
22
- query_and_build "release/#{id}", Discogs::Release
35
+ # Retrieves a master release by ID.
36
+ # @!macro [new] master_release_id
37
+ # @param master_release_id [Integer] master release id
38
+ # @return [Hash] the master release with provided master_release_id
39
+ def get_master_release(master_release_id)
40
+ query_and_build "masters/#{master_release_id}"
23
41
  end
24
42
 
25
- def get_master_release(id)
26
- query_and_build "master/#{id}", Discogs::MasterRelease
43
+ alias_method :get_master, :get_master_release
44
+
45
+ # Retrieves a list of all Releases that are versions of this master. Accepts Pagination parameters.
46
+ # @macro master_release_id
47
+ # @!macro [new] uses_pagination
48
+ # @param pagination [Hash] pagination parameters
49
+ # @return [Hash] the master release with the provided master_release_id, along with versions
50
+ def get_master_release_versions(master_release_id, pagination={})
51
+ query_and_build "masters/#{master_release_id}/versions", pagination
27
52
  end
28
53
 
29
- def get_artist(name)
30
- query_and_build "artist/#{name}", Discogs::Artist, {:releases => 1}
54
+ # Retrieves an artist by ID.
55
+ # @!macro [new] artist_id
56
+ # @param artist_id [Integer] artist id
57
+ # @return [Hash] the artist with provided artist_id
58
+ def get_artist(artist_id)
59
+ query_and_build "artists/#{artist_id}"
31
60
  end
32
61
 
33
- def get_label(name)
34
- query_and_build "label/#{name}", Discogs::Label, {:releases => 1}
62
+ # Returns a list of Releases and Masters associated with the artist. Accepts Pagination parameters.
63
+ # @macro artist_id
64
+ # @macro uses_pagination
65
+ # @return [Hash] the releases for artist with provided artist_id
66
+ def get_artists_releases(artist_id, pagination={})
67
+ query_and_build "artists/#{artist_id}/releases", pagination
35
68
  end
36
69
 
70
+ alias_method :get_artist_releases, :get_artists_releases
71
+
72
+ # Retrieves a label by ID.
73
+ # @!macro [new] label_id
74
+ # @param label_id [Integer] label id
75
+ # @return [Hash] the label with provided id
76
+ def get_label(label_id)
77
+ query_and_build "labels/#{label_id}"
78
+ end
79
+
80
+ # Returns a list of Releases associated with the label. Accepts Pagination parameters.
81
+ # @macro label_id
82
+ # @macro uses_pagination
83
+ # @return [Hash] the releases for label with provided id
84
+ def get_labels_releases(label_id, pagination={})
85
+ query_and_build "labels/#{label_id}/releases", pagination
86
+ end
87
+
88
+ alias_method :get_label_releases, :get_labels_releases
89
+
90
+ # Retrieve a user by username.
91
+ #
92
+ # If authenticated as the requested user, the email key will be visible.
93
+ #
94
+ # If authenticated as the requested user or the user’s collection/wantlist is public,
95
+ # the num_collection / num_wantlist keys will be visible.
96
+ #
97
+ # @!macro [new] username
98
+ # @param username [String] username
99
+ # @return [Hash] the user with provided username
37
100
  def get_user(username)
38
- query_and_build "users/#{username}", Discogs::User
101
+ query_and_build "users/#{username}"
102
+ end
103
+
104
+ # Get a collection for a user by username
105
+ #
106
+ # Shortcut method for #get_user_folder_releases[#get_user_folder_releases-instance_method]
107
+ #
108
+ # @macro username
109
+ # @macro uses_pagination
110
+ # @return [Hash] the user with provided username
111
+ def get_user_collection(username, pagination={})
112
+ get_user_folder_releases(username, 0)
113
+ end
114
+
115
+ # Retrieve a list of user-defined collection notes fields. These fields are available on every release in the collection.
116
+ #
117
+ # If the collection has been made private by its owner, authentication as the collection owner is required.
118
+ #
119
+ # If you are not authenticated as the collection owner, only fields with public set to true will be visible.
120
+ #
121
+ # @macro username
122
+ # @return [Hash] list of collection fields for the provided username
123
+ def get_user_collection_fields(username)
124
+ query_and_build "users/#{username}/collection/fields"
125
+ end
126
+
127
+ # Returns the list of releases in a user’s wantlist. Accepts Pagination parameters.
128
+ #
129
+ # Basic information about each release is provided, suitable for display in a list. For detailed information, make another API call to fetch the corresponding release.
130
+ #
131
+ # If the wantlist has been made private by its owner, you must be authenticated as the owner to view it.
132
+ #
133
+ # The notes field will be visible if you are authenticated as the wantlist owner.
134
+ #
135
+ # @macro username
136
+ # @macro uses_pagination
137
+ # @return [Hash] wantlist for the provided username
138
+ def get_user_wantlist(username, pagination={})
139
+ query_and_build "users/#{username}/wants", pagination
140
+ end
141
+
142
+ alias_method :get_user_wants, :get_user_wantlist
143
+
144
+ # Returns a specific release in a user’s wantlist by release id.
145
+ #
146
+ # If the wantlist has been made private by its owner, you must be authenticated as the owner to view it.
147
+ #
148
+ # The notes field will be visible if you are authenticated as the wantlist owner.
149
+ #
150
+ # @macro username
151
+ # @macro release_id
152
+ # @return [Hash] wantlist for the provided username
153
+ def get_user_want(username, release_id)
154
+ query_and_build "users/#{username}/wants/#{release_id}"
155
+ end
156
+
157
+ # Add a release to a user’s wantlist.
158
+ #
159
+ # @!macro [new] need_auth
160
+ # @note Authentication as the owner is required.
161
+ #
162
+ # @macro username
163
+ # @macro release_id
164
+ # @param [Hash] data optional parameters:
165
+ # @option data [String] :notes User notes to associate with this release.
166
+ # @option data [Integer] :rating User’s rating of this release, from 0 (unrated) to 5 (best). Defaults to 0.
167
+ # @return [Hash] new wantlist entry
168
+ def add_release_to_user_wantlist(username, release_id, data={})
169
+ authenticated? do
170
+ query_and_build "users/#{username}/wants/#{release_id}", {}, :put, data
171
+ end
172
+ end
173
+
174
+ # Edit the notes (or rating) on a release in a user’s wantlist.
175
+ #
176
+ # @macro need_auth
177
+ #
178
+ # @macro username
179
+ # @macro release_id
180
+ # @param data [Hash] optional parameters:
181
+ # @option data [String] :notes User notes to associate with this release.
182
+ # @option data [Integer] :rating User’s rating of this release, from 0 (unrated) to 5 (best). Defaults to 0.
183
+ # @return [Hash] updated wantlist entry
184
+ def edit_release_in_user_wantlist(username, release_id, data={})
185
+ authenticated? do
186
+ query_and_build "users/#{username}/wants/#{release_id}", {}, :post, data
187
+ end
188
+ end
189
+
190
+ # Remove a release from a user's wantlist.
191
+ #
192
+ # @macro need_auth
193
+ #
194
+ # @macro username
195
+ # @macro release_id
196
+ # @return [Boolean]
197
+ def delete_release_in_user_wantlist(username, release_id)
198
+ authenticated? do
199
+ query_and_build "users/#{username}/wants/#{release_id}", {}, :delete
200
+ end
201
+ end
202
+
203
+ alias_method :delete_release_from_user_wantlist, :delete_release_in_user_wantlist
204
+
205
+ # Retrieve basic information about the authenticated user.
206
+ #
207
+ # You can use this resource to find out who you’re authenticated as, and it also doubles as a good sanity check to ensure that you’re using OAuth correctly.
208
+ #
209
+ # For more detailed information, make another request for the user’s Profile.
210
+ #
211
+ # @macro need_auth
212
+ # @return [Hash] authenticated user information
213
+ def get_identity
214
+ authenticated? do
215
+ query_and_build "oauth/identity"
216
+ end
217
+ end
218
+
219
+ # Edit a user’s profile data.
220
+ #
221
+ # @macro need_auth
222
+ #
223
+ # @macro username
224
+ # @param [Hash] data data to update, with the optional keys:
225
+ # @option data [String] :name The real name of the user.
226
+ # @option data [String] :home_page The user's website.
227
+ # @option data [String] :location The geographical location of the user.
228
+ # @option data [String] :profile Biographical information about the user.
229
+ def edit_user(username, data={})
230
+ authenticated? do
231
+ query_and_build "users/#{username}", {}, :post, data
232
+ end
233
+ end
234
+
235
+ # Add a release to a folder in a user’s collection.
236
+ #
237
+ # @macro need_auth
238
+ #
239
+ # The folder_id must be non-zero – you can use 1 for “Uncategorized”.
240
+ #
241
+ # @macro username
242
+ # @!macro [new] folder_id
243
+ # @param folder_id [Integer] folder id
244
+ # @macro release_id
245
+ # @return [Hash] new instance metadata
246
+ def add_release_to_user_folder(username, folder_id, release_id)
247
+ authenticated? do
248
+ query_and_build "users/#{username}/collection/folders/#{folder_id}/releases/#{release_id}", {}, :post
249
+ end
250
+ end
251
+
252
+ alias_method :add_instance_to_user_folder, :add_release_to_user_folder
253
+
254
+ # Change the rating on a release and/or move the instance to another folder.
255
+ #
256
+ # @macro need_auth
257
+ #
258
+ # @macro username
259
+ # @macro folder_id
260
+ # @macro release_id
261
+ # @!macro [new] instance_id
262
+ # @param instance_id [Integer] instance id
263
+ # @param [Hash] data optional parameters
264
+ # @option data [Integer] :rating User’s rating of this release, from 0 (unrated) to 5 (best).
265
+ # @option data [Integer] :folder_id The ID of the folder to move the release into.
266
+ # @return [Boolean]
267
+ def edit_release_in_user_folder(username, folder_id, release_id, instance_id=1, data={})
268
+ authenticated? do
269
+ query_and_build "/users/#{username}/collection/folders/#{folder_id}/releases/#{release_id}/instances/#{instance_id}"
270
+ end
271
+ end
272
+
273
+ alias_method :edit_instance_in_user_folder, :edit_release_in_user_folder
274
+
275
+ # Remove an instance of a release from a user’s collection folder.
276
+ #
277
+ # To move the release to the “Uncategorized” folder instead, use the POST method.
278
+ #
279
+ # @macro need_auth
280
+ #
281
+ # @macro username
282
+ # @macro folder_id
283
+ # @macro release_id
284
+ # @macro instance_id
285
+ # @return [Boolean]
286
+ def delete_instance_in_user_folder(username, folder_id, release_id, instance_id)
287
+ authenticated? do
288
+ query_and_build "/users/#{username}/collection/folders/#{folder_id}/releases/#{release_id}/instances/#{instance_id}", {}, :delete
289
+ end
290
+ end
291
+
292
+ alias_method :delete_release_in_user_folder, :delete_instance_in_user_folder
293
+
294
+ # Change the value of a notes field on a particular instance.
295
+ #
296
+ # @macro need_auth
297
+ #
298
+ # @macro username
299
+ # @macro folder_id
300
+ # @macro release_id
301
+ # @macro instance_id
302
+ # @!macro [new] field_id
303
+ # @param field_id [Integer] field id
304
+ # @option data [String] :value The new value of the field. If the field’s type is dropdown, the value must match one of the values in the field’s list of options.
305
+ def edit_field_on_instance_in_user_folder(username, folder_id, release_id, instance_id, field_id, data={})
306
+ authenticated? do
307
+ query_and_build "/users/#{username}/collection/folders/#{folder_id}/releases/#{release_id}/instances/#{instance_id}/fields/#{field_id}", {}, :post, data
308
+ end
309
+ end
310
+
311
+ # Returns the list of releases in a folder in a user’s collection. Accepts Pagination parameters.
312
+ #
313
+ # Basic information about each release is provided, suitable for display in a list. For detailed information, make another API call to fetch the corresponding release.
314
+ #
315
+ # If folder_id is not 0, or the collection has been made private by its owner, authentication as the collection owner is required.
316
+ #
317
+ # If you are not authenticated as the collection owner, only public notes fields will be visible.
318
+ #
319
+ # @macro username
320
+ # @macro folder_id
321
+ # @param [Hash] params optional parameters
322
+ # @option params [String] :sort Sort items by this field. One of:
323
+ # * +label+
324
+ # * +artist+
325
+ # * +title+
326
+ # * +catno+
327
+ # * +format+
328
+ # * +rating+
329
+ # * +added+
330
+ # * +year+
331
+ # @option params [String] :sort_order Sort items in a particular order. One of:
332
+ # * +asc+
333
+ # * +desc+
334
+ def get_user_folder_releases(username, folder_id, params={})
335
+ if folder_id == 0 or authenticated?
336
+ query_and_build "users/#{username}/collection/folders/#{folder_id}/releases", params
337
+ else
338
+ raise_authentication_error
339
+ end
340
+ end
341
+
342
+ # Retrieve metadata about a folder in a user’s collection.
343
+ #
344
+ # If folder_id is not 0, authentication as the collection owner is required.
345
+ #
346
+ # @macro need_auth
347
+ #
348
+ # @macro username
349
+ # @macro folder_id
350
+ # @return [Hash] folder with folder_id
351
+ def get_user_folder(username, folder_id)
352
+ if folder_id == 0 or authenticated?
353
+ query_and_build "users/#{username}/collection/folders/#{folder_id}"
354
+ else
355
+ raise_authentication_error
356
+ end
39
357
  end
40
358
 
41
- def search(term, options={})
42
- opts = { :type => :all, :page => 1 }.merge(options)
43
- params = { :q => term, :type => opts[:type], :page => opts[:page] }
359
+ # Retrieve a list of folders in a user’s collection.
360
+ #
361
+ # If the collection has been made private by its owner, authentication as the collection owner is required.
362
+ #
363
+ # If you are not authenticated as the collection owner, only folder ID 0 (the “All” folder) will be visible.
364
+ #
365
+ # @macro username
366
+ # @return [Hash] folder listing
367
+ def get_user_folders(username)
368
+ query_and_build "users/#{username}/collection/folders"
369
+ end
370
+
371
+ # Create a new folder in a user’s collection.
372
+ #
373
+ # @macro need_auth
374
+ #
375
+ # @macro username
376
+ # @param [Hash] data folder parameters
377
+ # @option data [String] :name The name of the newly-created folder (Required).
378
+ # @return [Hash] new folder metadata
379
+ def create_user_folder(username, data={})
380
+ authenticated? do
381
+ query_and_build "users/#{username}/collection/folders", {}, :post, data
382
+ end
383
+ end
384
+
385
+ alias_method :add_user_folder, :create_user_folder
386
+
387
+ # Edit a folder’s metadata. Folders 0 and 1 cannot be renamed.
388
+ #
389
+ # @macro need_auth
390
+ #
391
+ # @macro username
392
+ # @macro folder_id
393
+ # @param [Hash] data folder parameters
394
+ # @option data [String] :name The name of the folder (Required).
395
+ # @return [Hash] updated folder metadata
396
+ def edit_user_folder(username, folder_id, data={})
397
+ authenticated? do
398
+ query_and_build "users/#{username}/collection/folders#{folder_id}", {}, :post, data
399
+ end
400
+ end
401
+
402
+ # Delete a folder from a user’s collection. A folder must be empty before it can be deleted.
403
+ #
404
+ # @macro need_auth
405
+ #
406
+ # @macro username
407
+ # @macro folder_id
408
+ # @return [Boolean]
409
+ def delete_user_folder(username, folder_id)
410
+ authenticated? do
411
+ query_and_build "users/#{username}/collection/folders#{folder_id}", {}, :delete
412
+ end
413
+ end
414
+
415
+ # Returns the list of listings in a user’s inventory. Accepts Pagination parameters.
416
+ #
417
+ # Basic information about each listing and the corresponding release is provided, suitable for display in a list. For detailed information about the release, make another API call to fetch the corresponding Release.
418
+ #
419
+ # If you are not authenticated as the inventory owner, only items that have a status of For Sale will be visible.
420
+ #
421
+ # If you are authenticated as the inventory owner you will get additional +weight+, +format_quantity+, and +external_id+ keys.
422
+ #
423
+ # @macro username
424
+ # @param [Hash] params sort/order/pagination parameters
425
+ # @option params [String] :status Only show items with this status
426
+ # @option params [String] :sort Sort items by this field. One of:
427
+ # * +listed+
428
+ # * +price+
429
+ # * +item+ (i.e. the title of the release)
430
+ # * +artist+
431
+ # * +label+
432
+ # * +catno+
433
+ # * +audio+
434
+ # * +status+ (when authenticated as inventory owner)
435
+ # @option params [String] :sort_order Sort items in a particular order. One of:
436
+ # * +asc+
437
+ # * +desc+
438
+ # @return [Hash] listing in user's inventory
439
+ def get_user_inventory(username, params={})
440
+ query_and_build "users/#{username}/inventory", params
441
+ end
442
+
443
+ # View the data associated with a listing.
444
+ #
445
+ # If the authorized user is the listing owner the listing will include the +weight+, +format_quantity+, and +external_id+ keys.
446
+ #
447
+ # @!macro [new] listing_id
448
+ # @param listing_id [Integer] listing id
449
+ # @return [Hash] listing with listing_id
450
+ def get_listing(listing_id)
451
+ query_and_build "marketplace/listings/#{listing_id}"
452
+ end
453
+
454
+ # Create a Marketplace listing.
455
+ #
456
+ # @macro need_auth
457
+ #
458
+ # @!macro [new] listing_params
459
+ # @param [Hash] data parameters for listing
460
+ # @option data [Integer (Required)] :release_id The ID of the release that this listing represents.
461
+ # @option data [String (Optional)] :condition The physical condition of the item. Must *EXACTLY* match one of:
462
+ # * +Mint (M)+
463
+ # * +Near Mint (NM or NM-)+
464
+ # * +Very Good Plus (VG+)+
465
+ # * +Very Good (VG)+
466
+ # * +Good Plus (G+)+
467
+ # * +Good (G)+
468
+ # * +Fair (F)+
469
+ # * +Poor (P)+
470
+ # @option data [String (Optional)] :sleeve_condition (+Not Graded+) The physical condition of the item's sleeve, case, or container. Must *EXACTLY* match one of:
471
+ # * +Mint (M)+
472
+ # * +Near Mint (NM or NM-)+
473
+ # * +Very Good Plus (VG+)+
474
+ # * +Very Good (VG)+
475
+ # * +Good Plus (G+)+
476
+ # * +Good (G)+
477
+ # * +Fair (F)+
478
+ # * +Poor (P)+
479
+ # * +Generic+
480
+ # * +Not Graded+
481
+ # * +No Cover+
482
+ # @option data [Float (Required)] :price The price of the item (in the seller's currency).
483
+ # @option data [String (Optional)] :comments Any remarks about the item that will be displayed to buyers.
484
+ # @option data [Boolean (Optional)] :allow_buffers (false) Whether or not to allow buyers to make offers on the item. Defaults to +false+.
485
+ # @option data [String (Optional)] :status (+For Sale+) The status of the listing. Defaults to For Sale. Must *EXACTLY* match one of:
486
+ # * +For Sale+ - the listing is ready to be shown on the Marketplace
487
+ # * +Draft+ - the listing is not ready for public display
488
+ # @option data [String (Optional)] :external_id A freeform field that can be used for the seller’s own reference. Information stored here will not be displayed to anyone other than the seller. This field is called “Private Comments” on the Discogs website.
489
+ # @option data [Float (Optional)] :weight The weight, in grams, of this listing, for the purpose of calculating shipping.
490
+ # @option data [Integer (Optional)] :format_quantity The number of items this listing counts as, for the purpose of calculating shipping. This field is called “Counts As” on the Discogs website.
491
+ # @return [Hash] listing metadata
492
+ def create_listing(data={})
493
+ authenticated? do
494
+ query_and_build "marketplace/listings", {}, :post, data
495
+ end
496
+ end
497
+
498
+ # Edit the data associated with a listing.
499
+ #
500
+ # If the listing’s status is not +For Sale+, +Draft+, or +Expired+, it cannot be modified – only deleted. To re-list a Sold listing, a new listing must be created.
501
+ #
502
+ # @macro need_auth
503
+ #
504
+ # @macro listing_id
505
+ # @macro listing_params
506
+ # @return [Boolean]
507
+ def edit_listing(listing_id, data={})
508
+ authenticated? do
509
+ query_and_build "marketplace/listings/#{listing_id}", {}, :post, data
510
+ end
511
+ end
512
+
513
+ # Permanently remove a listing from the Marketplace.
514
+ #
515
+ # @macro need_auth
516
+ #
517
+ # @macro listing_id
518
+ # @return [Boolean]
519
+ def delete_listing(listing_id)
520
+ authenticated? do
521
+ query_and_build "marketplace/listings/#{listing_id}", {}, :delete
522
+ end
523
+ end
524
+
525
+ # View the data associated with an order.
526
+ #
527
+ # @macro need_auth
528
+ #
529
+ # @!macro [new] order_id
530
+ # @param order_id [Integer] order id
531
+ # @return [Hash] order information
532
+ def get_order(order_id)
533
+ authenticated? do
534
+ query_and_build "marketplace/orders/#{order_id}"
535
+ end
536
+ end
537
+
538
+ # Edit the data associated with an order.
539
+ #
540
+ # The conditions under which an order is permitted to transition from one status to another are best summarized by a state diagram.
541
+ #
542
+ # http://www.discogs.com/developers/_images/order_state_transitions.png
543
+ #
544
+ # Rather than implementing this logic in your application, the response contains a next_status key – an array of valid next statuses for this order, which you can display to the user in (for example) a dropdown control. This also renders your application more resilient to any future changes in the order status logic.
545
+ #
546
+ # Changing the order status using this resource will always message the buyer with:
547
+ #
548
+ # Seller changed status from Old Status to New Status
549
+ #
550
+ # and does not provide a facility for including a custom message along with the change. For more fine-grained control, use the Add a new message resource, which allows you to simultaneously add a message and change the order status.
551
+ #
552
+ # If the order status is neither +cancelled+, +Payment Received+, nor +Shipped+, you can change the shipping. Doing so will send an invoice to the buyer and set the order status to Invoice Sent. (For that reason, you cannot set the shipping and the order status in the same request.)
553
+ #
554
+ # @macro need_auth
555
+ #
556
+ # @macro order_id
557
+ # @param [Hash] data order parameters
558
+ # @option data [String (Optional)] :status The new status of the order. Must *EXACTLY* match one of:
559
+ # * +New Order+
560
+ # * +Buyer Contacted+
561
+ # * +Invoice Sent+
562
+ # * +Payment Pending+
563
+ # * +Payment Received+
564
+ # * +Shipped+
565
+ # * +Cancelled (Non-Paying Buyer)+
566
+ # * +Cancelled (Item Unavailable)+
567
+ # * +Cancelled (Per Buyer's Request)+
568
+ # * the order's current status
569
+ # - Furthermore, the new status must be present in the order’s next_status list. For more information about order statuses, see #edit_order[#edit_order-instance_method].
570
+ # @option data [Float (Optional)] :shipping The order shipping amount.
571
+ #
572
+ # As a side-effect of setting this value, the buyer is invoiced and the order status is set to +Invoice Sent+. For more information, see #edit_order[#edit_order-instance_method].
573
+ # @return [Hash] order information
574
+ def edit_order(order_id, data={})
575
+ authenticated? do
576
+ query_and_build "marketplace/orders/#{order_id}", {}, :post, data
577
+ end
578
+ end
44
579
 
45
- data = query_api("search", params)
46
- resource = Discogs::Search.new(data)
580
+ # Returns a list of the authenticated user’s orders. Accepts Pagination parameters.
581
+ #
582
+ # @macro need_auth
583
+ #
584
+ # @param [Hash] params status, sort, sort_order, and pagination parameters
585
+ # @option params [String (Optional)] :status The new status of the order. Must *EXACTLY* match one of:
586
+ # * +All+
587
+ # * +New Order+
588
+ # * +Buyer Contacted+
589
+ # * +Invoice Sent+
590
+ # * +Payment Pending+
591
+ # * +Payment Received+
592
+ # * +Shipped+
593
+ # * +Merged+
594
+ # * +Order Changed+
595
+ # * +Cancelled+
596
+ # * +Cancelled (Non-Paying Buyer)+
597
+ # * +Cancelled (Item Unavailable)+
598
+ # * +Cancelled (Per Buyer's Request)+
599
+ # @option params [String (Optional)] :sort Sort items with this field. Must *EXACTLY* match one of:
600
+ # * +id+
601
+ # * +buyer+
602
+ # * +created+
603
+ # * +status+
604
+ # * +last_activity+
605
+ # @option params [String (Optional)] :sort_order Sort items in a particular order. Must *EXACTLY* match one of:
606
+ # * +asc+
607
+ # * +desc+
608
+ # @option params [Hash (Optional)] :pagination Pagination parameters
609
+ # @return [Hash] list of orders meeting specified criteria
610
+ def list_orders(params={})
611
+ authenticated? do
612
+ query_and_build "marketplace/orders", params
613
+ end
614
+ end
615
+
616
+ # Returns a list of the order’s messages with the most recent first. Accepts Pagination parameters.
617
+ #
618
+ # @macro need_auth
619
+ #
620
+ # @macro order_id
621
+ # @macro uses_pagination
622
+ # @return [Hash] messages for order
623
+ def list_order_messages(order_id, pagination={})
624
+ authenticated? do
625
+ query_and_build "marketplace/orders#{order_id}/messages", pagination
626
+ end
627
+ end
628
+
629
+ alias_method :get_order_messages, :list_order_messages
630
+
631
+ # Adds a new message to the order’s message log.
632
+ #
633
+ # When posting a new message, you can simultaneously change the order status. If you do, the message will automatically be prepended with:
634
+ # Seller changed status from Old Status to New Status\n\n
635
+ # While message and status are each optional, one or both must be present.
636
+ #
637
+ # @macro need_auth
638
+ #
639
+ # @macro order_id
640
+ # @param [Hash] data new message metadata
641
+ # @option data [String (Optional)] :message The body of the message to send to the buyer.
642
+ # @option data [String (Optional)] :status The new status of the corresponding order.
643
+ # * For more information about order statuses, see #edit_order[#edit_order-instance_method]
644
+ # @return [Hash] created message metadata
645
+ def create_order_message(order_id, data={})
646
+ authenticated? do
647
+ query_and_build "marketplace/orders/#{order_id}/messages", {}, :post, data
648
+ end
649
+ end
650
+
651
+ # Retrieve price suggestions for the provided Release ID. If no suggestions are available, an empty object will be returned.
652
+ #
653
+ # Authentication is required, and the user needs to have filled out their seller settings. Suggested prices will be denominated in the user’s selling currency.
654
+ #
655
+ # @macro need_auth
656
+ #
657
+ # @macro release_id
658
+ # @return [Hash] price suggestions information
659
+ def get_price_suggestions(release_id)
660
+ authenticated? do
661
+ query_and_build "marketplace/price_suggestions/#{release_id}"
662
+ end
663
+ end
664
+
665
+ # Calculate the fee for the provided price and currency.
666
+ #
667
+ # @param [Float] price price of item
668
+ # @param [String (Optional)] currency currency to return the fee in. Must *EXACTLY* match one of:
669
+ # * +USD+
670
+ # * +GBP+
671
+ # * +EUR+
672
+ # * +CAD+
673
+ # * +AUD+
674
+ # * +JPY+
675
+ def get_fee(price, currency="USD")
676
+ query_and_build "marketplace/fee/#{price}/#{currency}"
677
+ end
678
+
679
+ # Retrieve an image by filename.
680
+ #
681
+ # It’s unlikely that you’ll ever have to construct an image URL; images keys on other resources use fully-qualified URLs, including hostname and protocol.
682
+ #
683
+ # @macro need_auth
684
+ #
685
+ # @param [String (Required)] filename the name of the image file
686
+ # @return [Binary] binary image file
687
+ def get_image(filename)
688
+ authenticated? do
689
+ @access_token.get("/image/#{filename}").body
690
+ end
691
+ end
692
+
693
+ def search(term, params={})
694
+ parameters = {:q => term}.merge(params)
695
+
696
+ query_and_build "database/search", parameters
697
+ end
47
698
 
48
- resource.build_with_resp!
699
+ def raw(url)
700
+ uri = URI.parse(url)
701
+ params = CGI.parse(uri.query.to_s)
702
+
703
+ query_and_build uri.path, params
49
704
  end
50
705
 
51
706
  private
52
707
 
53
- def query_and_build(path, klass, params={})
54
- data = query_api(path, params)
55
- resource = klass.send(:new, data)
56
- resource.build!
708
+ def query_and_build(path, params={}, method=:get, body=nil)
709
+ parameters = {:f => "json"}.merge(params)
710
+ data = query_api(path, params, method, body)
711
+ hash = JSON.parse(data)
712
+
713
+ Hashie::Mash.new(hash)
57
714
  end
58
715
 
59
716
  # Queries the API and handles the response.
60
- def query_api(path, params={})
61
- response = make_request(path, params)
717
+ def query_api(path, params, method, body)
718
+ response = make_request(path, params, method, body)
62
719
 
63
720
  raise_unknown_resource(path) if response.code == "404"
721
+ raise_authentication_error(path) if response.code == "401"
64
722
  raise_internal_server_error if response.code == "500"
65
723
 
66
724
  # Unzip the response data, or just read it in directly
67
725
  # if the API responds without gzipping.
68
726
  response_body = nil
69
727
  begin
70
- inflated_data = Zlib::GzipReader.new(StringIO.new(response.body))
71
- response_body = inflated_data.read
728
+ inflated_data = Zlib::GzipReader.new(StringIO.new(response.body))
729
+ response_body = inflated_data.read
72
730
  rescue Zlib::GzipFile::Error
73
- response_body = response.body
731
+ response_body = response.body
74
732
  end
75
733
 
76
734
  response_body
77
735
  end
78
736
 
79
737
  # Generates a HTTP request and returns the response.
80
- def make_request(path, params={})
81
- uri = build_uri(path, params)
738
+ def make_request(path, params, method, body)
739
+ uri = build_uri(path, params)
740
+ formatted = "#{uri.path}?#{uri.query}"
741
+ output_format = params.fetch(:f, "json")
742
+ headers = {"Accept" => "application/#{output_format}",
743
+ "Accept-Encoding" => "gzip,deflate",
744
+ "User-Agent" => @app_name}
82
745
 
83
- request = Net::HTTP::Get.new(uri.path + "?" + uri.query)
84
- request.add_field("Accept", "application/xml")
85
- request.add_field("Accept-Encoding", "gzip,deflate")
86
- request.add_field("User-Agent", @app_name)
746
+ if authenticated?
747
+ if [:post, :put].include?(method)
748
+ headers["Content-Type"] = "application/json"
749
+ @access_token.send(method, formatted, JSON(body), headers)
750
+ else
751
+ @access_token.send(method, formatted, headers)
752
+ end
753
+ else
754
+ # All non-authenticated endpoints are GET.
755
+ request = Net::HTTP::Get.new(formatted)
87
756
 
88
- Net::HTTP.new(uri.host).start do |http|
89
- http.request(request)
757
+ headers.each do |h, v|
758
+ request.add_field(h, v)
759
+ end
760
+
761
+ Net::HTTP.new(uri.host).start do |http|
762
+ http.request(request)
763
+ end
90
764
  end
91
765
  end
92
766
 
93
767
  def build_uri(path, params={})
94
- parameters = { :f => "xml" }.merge(params)
95
- querystring = "?" + parameters.map { |key, value| "#{key}=#{value}" }.sort.join("&")
768
+ output_format = params.fetch(:f, "json")
769
+ parameters = {:f => output_format}.merge(params)
770
+ querystring = "?" + URI.encode_www_form(prepare_hash(parameters))
96
771
 
97
772
  URI.parse(File.join(@@root_host, URI.encode(sanitize_path(path, URI.escape(querystring)))))
98
773
  end
99
774
 
775
+ # Stringifies keys and sorts.
776
+ def prepare_hash(h)
777
+ result = {}
778
+
779
+ h.each do |k, v|
780
+ result[k.to_s] = v
781
+ end
782
+
783
+ result.sort
784
+ end
785
+
100
786
  def sanitize_path(*path_parts)
101
787
  clean_path = path_parts.map { |part| part.gsub(/\s/, '+') }
102
-
103
788
  clean_path.join
104
789
  end
105
790
 
106
- def raise_unknown_resource(path='')
791
+ def raise_unknown_resource(path="")
107
792
  raise Discogs::UnknownResource, "Unknown Discogs resource: #{path}"
108
793
  end
109
794
 
110
795
  def raise_internal_server_error
111
- raise Discogs::InternalServerError, "The remote server cannot complete the request"
796
+ raise Discogs::InternalServerError, "The API server cannot complete the request"
797
+ end
798
+
799
+ def raise_authentication_error(path="")
800
+ raise Discogs::AuthenticationError, "Authentication is required for this resource: #{path}"
112
801
  end
113
802
 
114
803
  end