parent
858d552240
commit
d77d28b101
50
README.md
50
README.md
@ -6,17 +6,22 @@ go-discogs is a Go client library for the [Discogs API](https://www.discogs.com/
|
|||||||
|
|
||||||
The lib is under MIT but be sure you are familiar with [Discogs API Terms of Use](https://support.discogs.com/hc/en-us/articles/360009334593-API-Terms-of-Use).
|
The lib is under MIT but be sure you are familiar with [Discogs API Terms of Use](https://support.discogs.com/hc/en-us/articles/360009334593-API-Terms-of-Use).
|
||||||
|
|
||||||
### Feauteres
|
### Features
|
||||||
* Database
|
* Database
|
||||||
* [Releases](#releases)
|
* [Releases](#releases)
|
||||||
* Release Rating
|
* Release Rating
|
||||||
* Master Releases
|
* Master Releases
|
||||||
* Master Versions
|
* Master Versions
|
||||||
* Artists
|
* Artists
|
||||||
* Artist Releases
|
* Artist Releases
|
||||||
* Label
|
* Label
|
||||||
* All Label Releases
|
* All Label Releases
|
||||||
* [Search](#search)
|
* [Search](#search)
|
||||||
|
* [User Collection](#user-collection)
|
||||||
|
* Collection Folders
|
||||||
|
* Folder
|
||||||
|
* Collection Items by Folder
|
||||||
|
* Collection Items by Release
|
||||||
|
|
||||||
Install
|
Install
|
||||||
--------
|
--------
|
||||||
@ -30,7 +35,7 @@ First of all import library and init client variable. According to discogs api d
|
|||||||
import "github.com/irlndts/go-discogs"
|
import "github.com/irlndts/go-discogs"
|
||||||
```
|
```
|
||||||
|
|
||||||
Some requests require authentification (as any user). According to [Discogs](https://www.discogs.com/developers/#page:authentication,header:authentication-discogs-auth-flow), to send requests with Discogs Auth, you have two options: sending your credentials in the query string with key and secret parameters or a [token parameter](https://www.discogs.com/settings/developers).
|
Some requests require authentication (as any user). According to [Discogs](https://www.discogs.com/developers/#page:authentication,header:authentication-discogs-auth-flow), to send requests with Discogs Auth, you have two options: sending your credentials in the query string with key and secret parameters or a [token parameter](https://www.discogs.com/settings/developers).
|
||||||
|
|
||||||
```go
|
```go
|
||||||
client, err := discogs.New(&discogs.Options{
|
client, err := discogs.New(&discogs.Options{
|
||||||
@ -88,3 +93,28 @@ Example
|
|||||||
fmt.Println(r.Title)
|
fmt.Println(r.Title)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### User Collection
|
||||||
|
|
||||||
|
Query a users [collection](https://www.discogs.com/developers#page:user-collection).
|
||||||
|
|
||||||
|
##### Collection Folders
|
||||||
|
Example
|
||||||
|
```go
|
||||||
|
collection, err := client.CollectionFolders("my_user")
|
||||||
|
```
|
||||||
|
##### Folder
|
||||||
|
Example
|
||||||
|
```go
|
||||||
|
folder, err := client.Folder("my_user", 0)
|
||||||
|
```
|
||||||
|
##### Collection Items by Folder
|
||||||
|
Example
|
||||||
|
```go
|
||||||
|
items, err := client.CollectionItemsByFolder("my_user", 0, &Pagination{Sort: "artist", SortOrder: "desc", PerPage: 2})
|
||||||
|
```
|
||||||
|
##### Collection Items by Release
|
||||||
|
Example
|
||||||
|
```go
|
||||||
|
items, err := client.CollectionItemsByRelease("my_user", 12934893)
|
||||||
|
```
|
||||||
|
@ -26,11 +26,13 @@ type Options struct {
|
|||||||
|
|
||||||
// Discogs is an interface for making Discogs API requests.
|
// Discogs is an interface for making Discogs API requests.
|
||||||
type Discogs interface {
|
type Discogs interface {
|
||||||
|
CollectionService
|
||||||
DatabaseService
|
DatabaseService
|
||||||
SearchService
|
SearchService
|
||||||
}
|
}
|
||||||
|
|
||||||
type discogs struct {
|
type discogs struct {
|
||||||
|
CollectionService
|
||||||
DatabaseService
|
DatabaseService
|
||||||
SearchService
|
SearchService
|
||||||
}
|
}
|
||||||
@ -62,6 +64,7 @@ func New(o *Options) (Discogs, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return discogs{
|
return discogs{
|
||||||
|
newCollectionService(o.URL + "/users"),
|
||||||
newDatabaseService(o.URL, cur),
|
newDatabaseService(o.URL, cur),
|
||||||
newSearchService(o.URL + "/database/search"),
|
newSearchService(o.URL + "/database/search"),
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
testUserAgent = "UnitTestClient/0.0.2"
|
testUserAgent = "UnitTestClient/0.0.2"
|
||||||
|
testUsername = "test_user"
|
||||||
testToken = ""
|
testToken = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,4 +19,7 @@ var (
|
|||||||
ErrUnauthorized = &Error{"authentication required"}
|
ErrUnauthorized = &Error{"authentication required"}
|
||||||
ErrCurrencyNotSupported = &Error{"currency does not supported"}
|
ErrCurrencyNotSupported = &Error{"currency does not supported"}
|
||||||
ErrUserAgentInvalid = &Error{"invalid user-agent"}
|
ErrUserAgentInvalid = &Error{"invalid user-agent"}
|
||||||
|
ErrInvalidReleaseID = &Error{"invalid release id"}
|
||||||
|
ErrInvalidSortKey = &Error{"invalid sort key"}
|
||||||
|
ErrInvalidUsername = &Error{"invalid username"}
|
||||||
)
|
)
|
||||||
|
@ -78,6 +78,7 @@ type Format struct {
|
|||||||
Descriptions []string `json:"descriptions"`
|
Descriptions []string `json:"descriptions"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Qty string `json:"qty"`
|
Qty string `json:"qty"`
|
||||||
|
Text string `json:"text,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Company ...
|
// Company ...
|
||||||
@ -130,8 +131,8 @@ type Page struct {
|
|||||||
|
|
||||||
// URLsList ...
|
// URLsList ...
|
||||||
type URLsList struct {
|
type URLsList struct {
|
||||||
Last string `json:"last"`
|
Last string `json:"last,omitempty"`
|
||||||
Next string `json:"next"`
|
Next string `json:"next,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version ...
|
// Version ...
|
||||||
@ -188,7 +189,9 @@ type ReleaseSource struct {
|
|||||||
|
|
||||||
// Pagination ...
|
// Pagination ...
|
||||||
type Pagination struct {
|
type Pagination struct {
|
||||||
Sort string // year, title, format
|
// TODO(irlndts): validate requested Sort
|
||||||
|
Sort string // year, title, format etc
|
||||||
|
// TODO(irlndts): validate requested SortOrder
|
||||||
SortOrder string // asc, desc
|
SortOrder string // asc, desc
|
||||||
Page int
|
Page int
|
||||||
PerPage int
|
PerPage int
|
||||||
|
File diff suppressed because one or more lines are too long
135
user_collection.go
Normal file
135
user_collection.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package discogs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CollectionService is an interface to work with collection.
|
||||||
|
type CollectionService interface {
|
||||||
|
// Retrieve a list of folders in a user’s collection.
|
||||||
|
// If folder_id is not 0, authentication as the collection owner is required.
|
||||||
|
CollectionFolders(username string) (*CollectionFolders, error)
|
||||||
|
// Retrieve a list of items in a folder in a user’s collection.
|
||||||
|
// If folderID is not 0, authentication with token is required.
|
||||||
|
CollectionItemsByFolder(username string, folderID int, pagination *Pagination) (*CollectionItems, error)
|
||||||
|
// Retrieve the user’s collection folders which contain a specified release.
|
||||||
|
// The releaseID must be non-zero.
|
||||||
|
CollectionItemsByRelease(username string, releaseID int) (*CollectionItems, error)
|
||||||
|
// Retrieve metadata about a folder in a user’s collection.
|
||||||
|
Folder(username string, folderID int) (*Folder, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type collectionService struct {
|
||||||
|
url string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCollectionService(url string) CollectionService {
|
||||||
|
return &collectionService{
|
||||||
|
url: url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Folder serves folder response from discogs.
|
||||||
|
type Folder struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Count int `json:"count"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ResourceURL string `json:"resource_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *collectionService) Folder(username string, folderID int) (*Folder, error) {
|
||||||
|
if username == "" {
|
||||||
|
return nil, ErrInvalidUsername
|
||||||
|
}
|
||||||
|
var folder *Folder
|
||||||
|
err := request(s.url+"/"+username+"/collection/folders/"+strconv.Itoa(folderID), nil, &folder)
|
||||||
|
return folder, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectionFolders serves collection response from discogs.
|
||||||
|
type CollectionFolders struct {
|
||||||
|
Folders []Folder `json:"folders"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *collectionService) CollectionFolders(username string) (*CollectionFolders, error) {
|
||||||
|
if username == "" {
|
||||||
|
return nil, ErrInvalidUsername
|
||||||
|
}
|
||||||
|
var collection *CollectionFolders
|
||||||
|
err := request(s.url+"/"+username+"/collection/folders", nil, &collection)
|
||||||
|
return collection, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectionItemSource ...
|
||||||
|
type CollectionItemSource struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
BasicInformation BasicInformation `json:"basic_information"`
|
||||||
|
DateAdded string `json:"date_added"`
|
||||||
|
FolderID int `json:"folder_id,omitempty"`
|
||||||
|
InstanceID int `json:"instance_id"`
|
||||||
|
Notes string `json:"notes,omitempty"`
|
||||||
|
Rating int `json:"rating"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BasicInformation ...
|
||||||
|
type BasicInformation struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Artists []ArtistSource `json:"artists"`
|
||||||
|
CoverImage string `json:"cover_image"`
|
||||||
|
Formats []Format `json:"formats"`
|
||||||
|
Labels []LabelSource `json:"labels"`
|
||||||
|
Genres []string `json:"genres"`
|
||||||
|
MasterID int `json:"master_id"`
|
||||||
|
MasterURL *string `json:"master_url"`
|
||||||
|
ResourceURL string `json:"resource_url"`
|
||||||
|
Styles []string `json:"styles"`
|
||||||
|
Thumb string `json:"thumb"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Year int `json:"year"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectionItems list of items in a user’s collection
|
||||||
|
type CollectionItems struct {
|
||||||
|
Pagination Page `json:"pagination"`
|
||||||
|
Items []CollectionItemSource `json:"releases"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// valid sort keys
|
||||||
|
// https://www.discogs.com/developers#page:user-collection,header:user-collection-collection-items-by-folder
|
||||||
|
var validItemsByFolderSort = map[string]struct{}{
|
||||||
|
"": struct{}{},
|
||||||
|
"label": struct{}{},
|
||||||
|
"artist": struct{}{},
|
||||||
|
"title": struct{}{},
|
||||||
|
"catno": struct{}{},
|
||||||
|
"format": struct{}{},
|
||||||
|
"rating": struct{}{},
|
||||||
|
"added": struct{}{},
|
||||||
|
"year": struct{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *collectionService) CollectionItemsByFolder(username string, folderID int, pagination *Pagination) (*CollectionItems, error) {
|
||||||
|
if username == "" {
|
||||||
|
return nil, ErrInvalidUsername
|
||||||
|
}
|
||||||
|
if pagination != nil {
|
||||||
|
if _, ok := validItemsByFolderSort[pagination.Sort]; !ok {
|
||||||
|
return nil, ErrInvalidSortKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var items *CollectionItems
|
||||||
|
err := request(s.url+"/"+username+"/collection/folders/"+strconv.Itoa(folderID)+"/releases", pagination.params(), &items)
|
||||||
|
return items, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *collectionService) CollectionItemsByRelease(username string, releaseID int) (*CollectionItems, error) {
|
||||||
|
if username == "" {
|
||||||
|
return nil, ErrInvalidUsername
|
||||||
|
}
|
||||||
|
if releaseID == 0 {
|
||||||
|
return nil, ErrInvalidReleaseID
|
||||||
|
}
|
||||||
|
var items *CollectionItems
|
||||||
|
err := request(s.url+"/"+username+"/collection/releases/"+strconv.Itoa(releaseID), nil, &items)
|
||||||
|
return items, err
|
||||||
|
}
|
174
user_collection_test.go
Normal file
174
user_collection_test.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
package discogs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CollectionServer(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "GET" {
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/users/" + testUsername + "/collection/folders":
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if _, err := io.WriteString(w, collectionJson); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case "/users/" + testUsername + "/collection/folders/0":
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if _, err := io.WriteString(w, folderJson); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case "/users/" + testUsername + "/collection/folders/0/releases":
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if _, err := io.WriteString(w, collectionItemsByFolderJson); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case "/users/" + testUsername + "/collection/releases/12934893":
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if _, err := io.WriteString(w, collectionItemsByRelease); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCollectionServiceFolder(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(CollectionServer))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
d := initDiscogsClient(t, &Options{URL: ts.URL})
|
||||||
|
|
||||||
|
folder, err := d.Folder(testUsername, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get folder: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
json, err := json.Marshal(folder)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to marshal folder: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJson(t, string(json), folderJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCollectionServiceCollectionFolders(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(CollectionServer))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
d := initDiscogsClient(t, &Options{URL: ts.URL})
|
||||||
|
|
||||||
|
collection, err := d.CollectionFolders(testUsername)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get collection: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
json, err := json.Marshal(collection)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to marshal collection: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJson(t, string(json), collectionJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCollectionServiceCollectionItemsByFolder(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(CollectionServer))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
d := initDiscogsClient(t, &Options{URL: ts.URL})
|
||||||
|
|
||||||
|
items, err := d.CollectionItemsByFolder(testUsername, 0, &Pagination{Sort: "artist", SortOrder: "desc", PerPage: 2})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get collection items: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
json, err := json.Marshal(items)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to marshal collection items: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJson(t, string(json), collectionItemsByFolderJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCollectionServiceCollectionItemsByFolderError(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(CollectionServer))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
d := initDiscogsClient(t, &Options{URL: ts.URL})
|
||||||
|
|
||||||
|
_, err := d.CollectionItemsByFolder(testUsername, 0, &Pagination{Sort: "invalid"})
|
||||||
|
if err != ErrInvalidSortKey {
|
||||||
|
t.Fatalf("err got=%s; want=%s", err, ErrInvalidSortKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCollectionServiceCollectionItemsByRelease(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(CollectionServer))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
d := initDiscogsClient(t, &Options{URL: ts.URL})
|
||||||
|
|
||||||
|
items, err := d.CollectionItemsByRelease(testUsername, 12934893)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get collection items: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
json, err := json.Marshal(items)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to marshal collection items: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
compareJson(t, string(json), collectionItemsByRelease)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCollectionServiceCollectionItemsByReleaseErrors(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(CollectionServer))
|
||||||
|
defer ts.Close()
|
||||||
|
d := initDiscogsClient(t, &Options{URL: ts.URL})
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
username string
|
||||||
|
releaseID int
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]testCase{
|
||||||
|
"invalid username": testCase{
|
||||||
|
username: "",
|
||||||
|
releaseID: 1,
|
||||||
|
err: ErrInvalidUsername,
|
||||||
|
},
|
||||||
|
"invalid release id": testCase{
|
||||||
|
username: "test-username",
|
||||||
|
releaseID: 0,
|
||||||
|
err: ErrInvalidReleaseID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
_, err := d.CollectionItemsByRelease(tc.username, tc.releaseID)
|
||||||
|
if err != tc.err {
|
||||||
|
t.Fatalf("err got=%s; want=%s", err, tc.err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user