68 Commits

Author SHA1 Message Date
Artem Piskun
77c264c993 DSCGS-31 Search fix (#32) 2019-10-29 13:34:43 +03:00
Abe Massry
84a3ecd4c5 add cover_image to search results (#30) 2019-09-02 18:15:44 +03:00
Abe Massry
f9bfcf8fba Update search example for current code behavior (#29) 2019-08-28 12:53:11 +03:00
irlndts
664896b3dd Golangci-lint fix 2019-06-18 15:08:55 +03:00
Artem Piskun
3c48a3e6d4 Merge pull request #28 from irlndts/RemoveVendor 2019-06-06 15:24:00 +03:00
irlndts
271bbe8b83 Travis version updated 2019-06-06 15:20:17 +03:00
irlndts
35dfbe02d1 Vendor removed, some cleanups 2019-06-06 15:18:10 +03:00
irlndts
618b882634 Use go mod 2018-12-11 16:47:31 +03:00
Artem Piskun
a4b6e4193a Merge pull request #27 from pluyckx/master
Add 'Artists' field to 'Track'
2018-04-30 21:27:27 +03:00
Philip Luyckx
e1e0446c29 Add 'Artists' field to 'Track'
Some tracks contain different artists than specified in the release. In
this case there is a field 'artists' in the JSON response. This field is
added to the 'Track' struct. When the field 'artists' is available in the
JSON response, the length of 'Artists' is not 0. Otherwise the 'Artists'
from the release should be used.

Signed-off-by: Philip Luyckx <philip.luyckx@gmail.com>
2018-04-30 10:37:04 +02:00
irlndts
dd501a3f65 fix 2018-04-10 15:39:54 +03:00
irlndts
110a37661a goreportcard 2018-03-28 20:18:24 +03:00
irlndts
3775bc39c7 travis pic 2018-03-28 20:13:31 +03:00
irlndts
e9ad282056 added travis-ci.yml 2018-03-28 20:11:47 +03:00
Artem Piskun
31a2e3b959 Merge pull request #26 from irlndts/APIErrors 2018-03-24 16:19:05 +03:00
irlndts
8e69f9334b Added several tests 2018-03-24 16:17:35 +03:00
irlndts
788518e3e2 Added several tests 2018-03-24 16:14:54 +03:00
irlndts
0a3a17b69d Lint errors fixed 2018-03-24 13:46:20 +03:00
Artem Piskun
ebfd052f67 Merge pull request #24 from irlndts/DSCGS-23
Move services to database
2018-03-21 21:18:54 +03:00
irlndts
c526ccd692 Move services to database 2018-03-21 21:17:40 +03:00
Artem Piskun
91d056b186 Merge pull request #22 from irlndts/DSCGS-20
Fix govet errors
2018-03-21 20:58:14 +03:00
irlndts
441de35813 Fix govet errors 2018-03-21 20:57:39 +03:00
Artem Piskun
872ff94673 Merge pull request #21 from irlndts/DSCGS-19
DSCGS-19 fix lint
2018-03-20 13:57:39 +03:00
irlndts
afd778f11c Cleanup 2018-03-20 13:56:17 +03:00
irlndts
dd29f70850 DSCGS-19 Fix golint errors 2018-03-17 23:12:25 +03:00
irlndts
d5ddddc09d Doc fixed 2018-03-17 22:41:43 +03:00
Artem Piskun
ede8271b88 Merge pull request #18 from irlndts/DSCGS-17 2018-03-17 22:01:09 +03:00
irlndts
b2674eb55e DSCGS-17 Use github.com/google/go-querystring/ 2018-03-17 21:58:56 +03:00
Artem Piskun
2bc2347afc Merge pull request #16 from irlndts/DSCGS-11 2018-03-17 21:00:22 +03:00
irlndts
62ba69198d DSCGS-11 Cleanup 2018-03-17 20:59:51 +03:00
irlndts
33699d6ad5 DSCGS-11 Cleanup 2018-03-17 20:57:41 +03:00
irlndts
4b824487ea DSCGS-11 Improve SearchService 2018-03-13 00:12:37 +03:00
irlndts
1091d9be02 DSCGS-11 Improve SearchService 2018-03-13 00:12:06 +03:00
Artem Piskun
b8ec73e2a0 Merge pull request #15 from irlndts/DSCGS-10 2018-03-12 23:33:10 +03:00
irlndts
7a214b4b15 DSCGS-10 Rewrite LabelService 2018-03-12 23:31:47 +03:00
Artem Piskun
fcb075f0f4 Merge pull request #13 from irlndts/DSCGS-9 2018-03-12 21:14:16 +03:00
irlndts
615c7561ad DSCGS-9 Rewrite MasterService 2018-03-12 21:13:09 +03:00
Artem Piskun
93ddab1b21 Merge pull request #12 from irlndts/DSCGS-7 2018-03-12 20:54:54 +03:00
irlndts
ad267389f6 DSCGS-7 Rewrite ArtistService like release service 2018-03-12 20:51:06 +03:00
Artem Piskun
8d359bb193 Merge pull request #6 from irlndts/Rewrite
Improvement and Rating
2018-03-12 20:00:30 +03:00
irlndts
4a80c3cc39 Rating 2018-03-12 19:51:37 +03:00
irlndts
69ef2a5d9a Release 2018-03-12 19:31:50 +03:00
irlndts
d1095bc769 Release improvement 2018-03-12 19:30:58 +03:00
irlndts
7d6af61b78 TEST 2018-02-20 20:00:24 +03:00
irlndts
6af125badd Improvements 2018-02-20 19:26:00 +03:00
irlndts
916e1f0bd6 Improvement token 2018-02-20 18:16:34 +03:00
irlndts
d0f617733a Some Improvements 2018-02-20 18:13:04 +03:00
irlndts
055007c459 improvement 2018-01-22 21:15:06 +03:00
irlndts
72a2e61894 Doc fixed 2018-01-22 21:03:06 +03:00
irlndts
87747a846f Doc fixed 2018-01-22 21:01:29 +03:00
irlndts
795cdb9f79 doc updated 2018-01-05 22:52:20 +03:00
irlndts
6a5e9d0a6b fix 2017-05-10 18:02:10 +03:00
irlndts
7536e26d28 merge 2017-05-09 18:54:15 +03:00
irlndts
5f77b45281 merge 2017-05-09 18:53:47 +03:00
irlndts
0f41f45197 minor comment fix 2017-05-09 18:52:58 +03:00
irlndts
efff71f46e minor comments 2017-05-09 17:07:11 +03:00
Artem Piskun
86244f6665 Merge pull request #5 from irlndts/DSCG-4
DSCGS-4
2017-04-26 16:02:07 +03:00
irlndts
78b3d0b0ad DSCGS-4 fix 2017-04-26 16:01:06 +03:00
irlndts
44db789a6b DSCGS-4 Logic improved 2017-04-26 15:57:03 +03:00
irlndts
dd4fa14f61 DSCGS-4 enough for today 2017-04-25 19:39:32 +03:00
irlndts
26dbacfe44 Minor fixings 2017-04-17 00:09:01 +03:00
irlndts
bce16b5ed1 Some comments edited 2017-04-16 23:49:50 +03:00
Artem Piskun
4e7c6c065a Merge pull request #3 from irlndts/DISCOGS-2
Discogs-2 Search Implementation
2017-02-14 22:05:07 +03:00
irlndts
d5292c30c9 DISCOGS-2 Search implementation 2017-02-14 22:01:50 +03:00
irlndts
20710e3f3e DISCOGS-2 Fix for the day 2017-02-13 19:40:27 +03:00
irlndts
cef1beeb81 test is fixed 2017-02-10 19:54:21 +03:00
Artem Piskun
83d3334321 Update README.md 2016-03-14 14:19:41 +03:00
Artem Piskun
c131693a53 Update README.md 2016-03-11 17:54:33 +03:00
21 changed files with 818 additions and 575 deletions

3
.travis.yml Normal file
View File

@@ -0,0 +1,3 @@
language: go
go:
- "1.12.5"

View File

@@ -1,63 +1,89 @@
# REST API 2.0 Discogs.com client
go-discogs is a Go client library for the [Discogs API](https://www.discogs.com/developers/). Check the usage section or try the examples to see how to access the Discogs API.
[![Build Status](https://travis-ci.org/irlndts/go-discogs.svg?branch=master)](https://travis-ci.org/irlndts/go-discogs)[![Go Report Card](https://goreportcard.com/badge/github.com/irlndts/go-discogs)](https://goreportcard.com/report/github.com/irlndts/go-discogs)
go-discogs is a Go client library for the [Discogs API](https://www.discogs.com/developers/). Check the usage section to see how to access the Discogs API.
### Feauteres
* Database
* Releases
* [Releases](#releases)
* Release Rating
* Master Releases
* Release Versions
* Master Versions
* Artists
* Artist Releases
* Label
* All Label Releases
#### ToDo
- Search
* [Search](#search)
Install
--------
go get github.com/irlndts/go-discogs
go get -u github.com/irlndts/go-discogs
Usage
---------
The discogs package provides a client for accessing the Discogs API.
First of all import library and init client variable. According to discogs api documentation you [must provide your user-agent](https://www.discogs.com/developers/#page:home,header:home-general-information).
```go
package main
import (
"github.com/irlndts/go-discogs"
)
```
```go
client := discogs.NewClient().UserAgent("TestDiscogsClient/0.0.1 +example.com")
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).
This is token way example:
```go
client, err := discogs.NewClient(&discogs.Options{
UserAgent: "Some Name",
Currency: "EUR", // optional, "USD" (default), "GBP", "EUR", "CAD", "AUD", "JPY", "CHF", "MXN", "BRL", "NZD", "SEK", "ZAR" are allowed
Token: "Some Token", // optional
})
```
#### Releases
```go
params := &discogs.ReleaseParams{Release_id: "8138518"}
release, _, err := d.Release.Release(params)
fmt.Println(fmt.Println(release.Artists[0].Name, " - ", release.Title)) // St. Petersburg Ska-Jazz Review - Elephant Riddim
release, _ := client.Database.Release(9893847)
fmt.Println(release.Artists[0].Name, " - ", release.Title)
// St. Petersburg Ska-Jazz Review - Elephant Riddim
```
#### Artists
#### Search
Issue a search query to discogs database. This endpoint accepts pagination parameters.
Authentication (as any user) is required.
Use `SearchRequest` struct to create a request.
```go
params := &discogs.LabelParams{Label_id: "890477", Page: 2, Per_page: 3}
label, _, err := d.Label.Releases(params)
type SearchRequest struct {
Q string // search query (optional)
Type string // one of release, master, artist, label (optional)
Title string // search by combined “Artist Name - Release Title” title field (optional)
ReleaseTitle string // search release titles (optional)
Credit string // search release credits (optional)
Artist string // search artist names (optional)
Anv string // search artist ANV (optional)
Label string // search label names (optional)
Genre string // search genres (optional)
Style string // search styles (optional)
Country string // search release country (optional)
Year string // search release year (optional)
Format string // search formats (optional)
Catno string // search catalog number (optional)
Barcode string // search barcodes (optional)
Track string // search track titles (optional)
Submitter string // search submitter username (optional)
Contributer string // search contributor usernames (optional)
for _, release := range label.Releases {
fmt.Println(release.Title)
Page int // optional
PerPage int // optional
}
```
Example
```go
request := discogs.SearchRequest{Artist: "reggaenauts", ReleaseTitle: "river rock", Page: 0, PerPage: 1}
search, _ := client.Search.Search(request)
for _, r := range search.Results {
fmt.Println(r.Title)
}
/*
Someday / I Hate Everything About You
Spy Potion
Surf Attack From Russia
*/
```
etc.
More examples - soon

View File

@@ -1,58 +0,0 @@
package discogs
import (
"github.com/irlndts/go-apirequest"
"net/http"
)
type ArtistService struct {
api *apirequest.API
}
type ArtistParams struct {
Artist_id string
Sort string // year, title, format
Sort_order string // asc, desc
Page int
Per_page int
}
type Artist struct {
Namevariations []string `json:"namevariations"`
Profile string `json:"profile"`
Releases_url string `json:"releases_url"`
Resource_url string `json:"resource_url"`
Uri string `json:"uri"`
Urls []string `json:"urls"`
Data_quality string `json:"data_quality"`
Id int `json:"id"`
Images []Image `json:"images"`
Members []Member `json:"members"`
}
type ArtistReleases struct {
Paginastion Page `json:"pagination"`
Releases []ReleaseSource `json:"releases"`
}
func newArtistService(api *apirequest.API) *ArtistService {
return &ArtistService{
api: api.Path("artists/"),
}
}
func (self *ArtistService) Artist(params *ArtistParams) (*Artist, *http.Response, error) {
artist := new(Artist)
apiError := new(APIError)
resp, err := self.api.New().Get(params.Artist_id).Receive(artist, apiError)
return artist, resp, relevantError(err, *apiError)
}
func (self *ArtistService) Releases(params *ArtistParams) (*ArtistReleases, *http.Response, error) {
releases := new(ArtistReleases)
apiError := new(APIError)
resp, err := self.api.New().Get(params.Artist_id+"/releases").QueryStruct(params).Receive(releases, apiError)
return releases, resp, relevantError(err, *apiError)
}

View File

@@ -1,26 +0,0 @@
package discogs
import (
"fmt"
"testing"
)
func TestArtistService_Artist(t *testing.T) {
expectedId := 1000
d := NewClient().UserAgent("UnitTestClient/0.0.1 +https://github.com/irlndts/go-discogs")
artist, _, err := d.Artist.Artist(&ArtistParams{Artist_id: "1000"})
check(t, err)
assert(t, artist.Id == expectedId, fmt.Sprintf("Release.Title looked for %s, and received %s ", expectedId, artist.Id))
}
func TestArtistService_Releases(t *testing.T) {
expectedArtist := "Dave Clarke"
d := NewClient().UserAgent("UnitTestClient/0.0.1 +https://github.com/irlndts/go-discogs")
releases, _, err := d.Artist.Releases(&ArtistParams{Artist_id: "1000", Sort: "year", Sort_order: "desc"})
check(t, err)
assert(t, releases.Releases[0].Artist == expectedArtist, fmt.Sprintf("Releses.Artist looked for %s, and received %s ", expectedArtist, releases.Releases[0].Artist))
}

View File

@@ -1,143 +0,0 @@
package discogs
type Video struct {
Description string `json:"description"`
Duration int `json:"duration"`
Embed bool `json:"embed"`
Title string `json:"title"`
Uri string `json:"uri"`
}
type ArtistSource struct {
Anv string `json:"anv"`
Id int `json:"id"`
Join string `json:"join"`
Name string `json:"name:`
Resource_url string `json:"resource_url"`
Role string `json:"role"`
Tracks string `json:"tracks"`
}
type Image struct {
Height int `json:"height"`
Width int `json:"width"`
Resource_url string `json:"resource_url"`
Type string `json:"type"`
Uri string `json:"uri"`
Uri150 string `json:"uri150"`
}
type Track struct {
Duration string `json:"duration"`
Position string `json:"position"`
Title string `json:"title"`
Type string `json:"type_"`
Extraartists []ArtistSource `json:"extraartists"`
}
type LabelSource struct {
Catno string `json:"catno"`
Entity_type string `json:"entity_type"`
Id int `json:"id"`
Name string `json:"name"`
Resource_url string `json:"resource_url"`
}
type Identifier struct {
Type string `json:"type"`
Value string `json:"value"`
}
type Format struct {
Descriptions []string `json:"descriptions"`
Name string `json:"name"`
Qty string `json:"qty"`
}
type Company struct {
Catno string `json:"catno"`
Entity_type string `json:"entity_type"`
Entity_type_name string `json:"entity_type_name"`
Id int `json:"id"`
Name string `json:"name"`
Resource_url string `json:"resource_url"`
}
type Community struct {
Contributors []Contributor `json:"contributors"`
Data_quality string `json:"string"`
Have int `json:"have"`
Rating Rating `json:"rating"`
Status string `json:"status"`
Submitter Submitter `json:"submitter"`
Want int `json:"want"`
}
type Submitter struct {
Resource_url string `json:"resource_url"`
Username string `json:"username"`
}
type Rating struct {
Average float32 `json:"average"`
Count int `json:"count"`
}
type Contributor struct {
Resource_url string `json:"resource_url"`
Username string `json:"username"`
}
type Page struct {
Per_page int `json:"per_page"`
Items int `json:"items"`
Page int `json:"page"`
Urls URLS `json:"urls"`
Pages int `json:"pages"`
}
type URLS struct {
Last string `json:"last"`
Next string `json:"next"`
}
type Version struct {
Catno string `json:"catno"`
Country string `json:"country"`
Format string `json:"format"`
Id int `json:"id"`
Label string `json:"label"`
Released string `json:"released"`
Resource_url string `json:"resource_url"`
Status string `json:"status"`
Thumb string `json:"thumb"`
Title string `json:"title"`
}
type Member struct {
Active bool `json:"active"`
Id int `json:"id"`
Name string `json:"name"`
Resource_url string `json:"resource_url"`
}
type Sublable struct {
Resource_url string `json:"url"`
Id int `json:"id"`
Name string `json:"name"`
}
type ReleaseSource struct {
Artist string `json:"artist"`
Catno string `json:"catno"`
Format string `json:"format"`
Id int `json:"id"`
Resource_url string `json:"resource_url"`
Status string `json:"status"`
Thumb string `json:"thumb"`
Title string `json:"title"`
Year int `json:"year"`
Main_release int `json:"main_release"`
Role string `json:"role"`
Type string `json:"type"`
}

196
database.go Normal file
View File

@@ -0,0 +1,196 @@
package discogs
import (
"net/url"
"strconv"
)
const (
releasesURI = "/releases/"
artistsURI = "/artists/"
labelsURI = "/labels/"
mastersURI = "/masters/"
)
// DatabaseService ...
type DatabaseService struct {
url string
currency string
}
func newDatabaseService(url string, currency string) *DatabaseService {
return &DatabaseService{
url: url,
currency: currency,
}
}
// Release serves relesase response from discogs
type Release struct {
Title string `json:"title"`
ID int `json:"id"`
Artists []ArtistSource `json:"artists"`
DataQuality string `json:"data_quality"`
Thumb string `json:"thumb"`
Community Community `json:"community"`
Companies []Company `json:"companies"`
Country string `json:"country"`
DateAdded string `json:"date_added"`
DateChanged string `json:"date_changed"`
EstimatedWeight int `json:"estimated_weight"`
ExtraArtists []ArtistSource `json:"extraartists"`
FormatQuantity int `json:"format_quantity"`
Formats []Format `json:"formats"`
Genres []string `json:"genres"`
Identifiers []Identifier `json:"identifiers"`
Images []Image `json:"images"`
Labels []LabelSource `json:"labels"`
LowestPrice float64 `json:"lowest_price"`
MasterID int `json:"master_id"`
MasterURL string `json:"master_url"`
Notes string `json:"notes,omitempty"`
NumForSale int `json:"num_for_sale,omitempty"`
Released string `json:"released"`
ReleasedFormatted string `json:"released_formatted"`
ResourceURL string `json:"resource_url"`
// Series
Status string `json:"status"`
Styles []string `json:"styles"`
Tracklist []Track `json:"tracklist"`
URI string `json:"uri"`
Videos []Video `json:"videos"`
Year int `json:"year"`
}
// Release returns release by release's ID
func (s *DatabaseService) Release(releaseID int) (*Release, error) {
params := url.Values{}
params.Set("curr_abbr", s.currency)
var release *Release
err := request(s.url+releasesURI+strconv.Itoa(releaseID), params, &release)
return release, err
}
// ReleaseRating serves response for community release rating request
type ReleaseRating struct {
ID int `json:"release_id"`
Rating Rating `json:"rating"`
}
// ReleaseRating retruns community release rating
func (s *DatabaseService) ReleaseRating(releaseID int) (*ReleaseRating, error) {
var rating *ReleaseRating
err := request(s.url+releasesURI+strconv.Itoa(releaseID)+"/rating", nil, &rating)
return rating, err
}
// Artist ...
type Artist struct {
Namevariations []string `json:"namevariations"`
Profile string `json:"profile"`
ReleasesURL string `json:"releases_url"`
ResourceURL string `json:"resource_url"`
URI string `json:"uri"`
URLs []string `json:"urls"`
DataQuality string `json:"data_quality"`
ID int `json:"id"`
Images []Image `json:"images"`
Members []Member `json:"members"`
}
// Artist represents a person in the discogs database
func (s *DatabaseService) Artist(artistID int) (*Artist, error) {
var artist *Artist
err := request(s.url+artistsURI+strconv.Itoa(artistID), nil, &artist)
return artist, err
}
// ArtistReleases ...
type ArtistReleases struct {
Pagination Page `json:"pagination"`
Releases []ReleaseSource `json:"releases"`
}
// ArtistReleases returns a list of releases and masters associated with the artist.
func (s *DatabaseService) ArtistReleases(artistID int, pagination *Pagination) (*ArtistReleases, error) {
var releases *ArtistReleases
err := request(s.url+artistsURI+strconv.Itoa(artistID)+"/releases", pagination.params(), &releases)
return releases, err
}
// Label resource represents a label, company, recording studio, location,
// or other entity involved with artists and releases.
type Label struct {
Profile string `json:"profile"`
ReleasesURL string `json:"releases_url"`
Name string `json:"name"`
ContactInfo string `json:"contact_info"`
URI string `json:"uri"`
Sublabels []Sublable `json:"sublabels"`
URLs []string `json:"urls"`
Images []Image `json:"images"`
ResourceURL string `json:"resource_url"`
ID int `json:"id"`
DataQuality string `json:"data_quality"`
}
// Label returns a label.
func (s *DatabaseService) Label(labelID int) (*Label, error) {
var label *Label
err := request(s.url+labelsURI+strconv.Itoa(labelID), nil, &label)
return label, err
}
// LabelReleases is a list of Releases associated with the label.
type LabelReleases struct {
Pagination Page `json:"pagination"`
Releases []ReleaseSource `json:"releases"`
}
// LabelReleases returns a list of Releases associated with the label.
func (s *DatabaseService) LabelReleases(labelID int, pagination *Pagination) (*LabelReleases, error) {
var releases *LabelReleases
err := request(s.url+labelsURI+strconv.Itoa(labelID)+"/releases", pagination.params(), &releases)
return releases, err
}
// Master resource represents a set of similar releases.
// Masters (also known as `master releases`) have a `main release` which is often the chronologically earliest.
type Master struct {
Styles []string `json:"styles"`
Genres []string `json:"genres"`
Videos []Video `json:"videos"`
Title string `json:"title"`
MainRelease int `json:"main_release"`
MainReleaseURL string `json:"main_release_url"`
URI string `json:"uri"`
Artists []Artist `json:"artists"`
VersionURL string `json:"version_url"`
Year int `json:"year"`
Images []Image `json:"images"`
ResourceURL string `json:"resource_url"`
Tracklist []Track `json:"tracklist"`
ID int `json:"id"`
DataQuality string `json:"data_quality"`
}
// Master returns a master release
func (s *DatabaseService) Master(masterID int) (*Master, error) {
var master *Master
err := request(s.url+mastersURI+strconv.Itoa(masterID), nil, &master)
return master, err
}
// MasterVersions retrieves a list of all releases that are versions of this master.
type MasterVersions struct {
Pagination Page `json:"pagination"`
Versions []Version `json:"versions"`
}
// MasterVersions retrieves a list of all Releases that are versions of this master
func (s *DatabaseService) MasterVersions(masterID int, pagination *Pagination) (*MasterVersions, error) {
var versions *MasterVersions
err := request(s.url+mastersURI+strconv.Itoa(masterID)+"/versions", pagination.params(), &versions)
return versions, err
}

31
database_test.go Normal file
View File

@@ -0,0 +1,31 @@
package discogs
import (
"io"
"net/http"
"net/http/httptest"
"testing"
)
func ReleaseServer(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
if _, err := io.WriteString(w, `{"title":"Elephant Riddim"}`); err != nil {
panic(err)
}
}
func TestReleaseServiceRelease(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(ReleaseServer))
defer ts.Close()
expectedTitle := "Elephant Riddim"
d := initDiscogsClient(t, &Options{URL: ts.URL})
release, err := d.Database.Release(8138518)
if err != nil {
t.Fatalf("failed to get release: %s", err)
}
if release.Title != expectedTitle {
t.Fatalf("release title got=%s want=%s ", expectedTitle, release.Title)
}
}

View File

@@ -1,39 +1,104 @@
package discogs
import (
"github.com/irlndts/go-apirequest"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
const (
discogsAPI = "https://api.discogs.com/"
useragent = "Test UserAgent"
discogsAPI = "https://api.discogs.com"
)
// Options is a set of options to use discogs API client
type Options struct {
URL string
Currency string
UserAgent string
Token string
}
// Client is a Discogs client for making Discogs API requests.
type Client struct {
api *apirequest.API
Release *ReleaseService
Master *MasterService
Artist *ArtistService
Label *LabelService
Database *DatabaseService
Search *SearchService
}
var header *http.Header
// NewClient returns a new Client.
func NewClient() *Client {
base := apirequest.New().Client(&http.Client{}).Base(discogsAPI).Add("User-Agent", useragent)
func NewClient(o *Options) (*Client, error) {
header = &http.Header{}
if o == nil || o.UserAgent == "" {
return nil, ErrUserAgentInvalid
}
header.Add("User-Agent", o.UserAgent)
cur, err := currency(o.Currency)
if err != nil {
return nil, err
}
// set token, it's required for some queries like search
if o.Token != "" {
header.Add("Authorization", "Discogs token="+o.Token)
}
if o.URL == "" {
o.URL = discogsAPI
}
return &Client{
api: base,
Release: newReleaseService(base.New()),
Master: newMasterService(base.New()),
Artist: newArtistService(base.New()),
Label: newLabelService(base.New()),
Database: newDatabaseService(o.URL, cur),
Search: newSearchService(o.URL + "/database/search"),
}, nil
}
// currency validates currency for marketplace data.
// Defaults to the authenticated users currency. Must be one of the following:
// USD GBP EUR CAD AUD JPY CHF MXN BRL NZD SEK ZAR
func currency(c string) (string, error) {
switch c {
case "USD", "GBP", "EUR", "CAD", "AUD", "JPY", "CHF", "MXN", "BRL", "NZD", "SEK", "ZAR":
return c, nil
case "":
return "USD", nil
default:
return "", ErrCurrencyNotSupported
}
}
// discogs require specified user agent
func (c *Client) UserAgent(useragent string) *Client {
c.api.Set("User-Agent", useragent)
return c
func request(path string, params url.Values, resp interface{}) error {
r, err := http.NewRequest("GET", path+"?"+params.Encode(), nil)
if err != nil {
return err
}
r.Header = *header
client := &http.Client{}
response, err := client.Do(r)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
switch response.StatusCode {
case http.StatusUnauthorized:
return ErrUnauthorized
default:
return fmt.Errorf("unknown error: %s", response.Status)
}
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
return json.Unmarshal(body, &resp)
}

View File

@@ -4,14 +4,90 @@ import (
"testing"
)
func check(t *testing.T, e error) {
if e != nil {
t.Error(e)
const (
testUserAgent = "UnitTestClient/0.0.2 +https://github.com/irlndts/go-discogs"
testToken = ""
)
func initDiscogsClient(t *testing.T, options *Options) *Client {
if options == nil {
options = &Options{
UserAgent: testUserAgent,
Currency: "USD",
Token: testToken,
}
}
if options.UserAgent == "" {
options.UserAgent = testUserAgent
}
client, err := NewClient(options)
if err != nil {
t.Fatalf("failed to create client: %s", err)
}
return client
}
func TestNewClient(t *testing.T) {
tests := map[string]struct {
options *Options
err error
}{
"normal": {&Options{
UserAgent: testUserAgent,
Currency: "USD",
Token: "some token",
}, nil},
"incorrect user-agent": {&Options{
UserAgent: "",
Currency: "USD",
}, ErrUserAgentInvalid},
"incorrect currency": {&Options{
UserAgent: testUserAgent,
Currency: "RUR",
}, ErrCurrencyNotSupported},
}
for name := range tests {
tt := tests[name]
t.Run(name, func(t *testing.T) {
if _, err := NewClient(tt.options); err != tt.err {
t.Errorf("err got=%s; want=%s", err, tt.err)
}
})
}
}
func assert(t *testing.T, condition bool, assertion string) {
if !condition {
t.Errorf("Assertion failed: %v", assertion)
func TestCurrency(t *testing.T) {
tests := []struct {
currency string
want string
err error
}{
{currency: "", want: "USD", err: nil},
{currency: "USD", want: "USD", err: nil},
{currency: "GBP", want: "GBP", err: nil},
{currency: "EUR", want: "EUR", err: nil},
{currency: "CAD", want: "CAD", err: nil},
{currency: "AUD", want: "AUD", err: nil},
{currency: "JPY", want: "JPY", err: nil},
{currency: "CHF", want: "CHF", err: nil},
{currency: "MXN", want: "MXN", err: nil},
{currency: "BRL", want: "BRL", err: nil},
{currency: "NZD", want: "NZD", err: nil},
{currency: "SEK", want: "SEK", err: nil},
{currency: "ZAR", want: "ZAR", err: nil},
{currency: "RUR", want: "", err: ErrCurrencyNotSupported},
}
for i, tt := range tests {
cur, err := currency(tt.currency)
if err != tt.err {
t.Errorf("#%d err got=%s; want=%s", i, err, tt.err)
}
if cur != tt.want {
t.Errorf("#%d currency got=%s; want=%s", i, cur, tt.want)
}
}
}

View File

@@ -2,38 +2,21 @@ package discogs
import (
"fmt"
"strings"
)
// APIError represents a Discogs API Error response
type APIError struct {
Message string `json:"message"`
// Error represents a Discogs API error
type Error struct {
Message string
}
func (e APIError) Error() string {
if e.Message != "" {
return fmt.Sprintf("discogs: %v", e.Message)
}
return ""
func (e *Error) Error() string {
return fmt.Sprintf("discogs error: %s", strings.ToLower(e.Message))
}
// Empty returns true if empty. Otherwise, at least 1 error message/code is
// present and false is returned.
func (e APIError) Empty() bool {
if e.Message == "" {
return true
}
return false
}
// relevantError returns any non-nil http-related error (creating the request,
// getting the response, decoding) if any. If the decoded apiError is non-zero
// the apiError is returned. Otherwise, no errors occurred, returns nil.
func relevantError(httpError error, apiError APIError) error {
if httpError != nil {
return httpError
}
if apiError.Empty() {
return nil
}
return apiError
}
// APIErrors
var (
ErrUnauthorized = &Error{"authentication required"}
ErrCurrencyNotSupported = &Error{"currency does not supported"}
ErrUserAgentInvalid = &Error{"invalid user-agent"}
)

View File

@@ -2,24 +2,25 @@ package main
import (
"fmt"
"github.com/irlndts/go-discogs"
)
func main() {
d := discogs.NewClient().UserAgent("TestDiscogsClient/0.0.1 +http://irlndts.moscow")
/*
params := &discogs.ReleaseParams{Release_id: "8138518"}
release, _, err := d.Release.Release(params)
*/
params := &discogs.LabelParams{Label_id: "890477", Page: 2, Per_page: 3}
label, _, err := d.Label.Releases(params)
d, err := discogs.NewClient(&discogs.Options{
UserAgent: "TestDiscogsClient/0.0.1 +http://example.com",
Currency: "USD",
Token: "",
})
if err != nil {
fmt.Println(err)
} else {
for _, release := range label.Releases {
fmt.Println(release.Title)
}
return
}
release, err := d.Search.Search(discogs.SearchRequest{Q: "middle", PerPage: 3})
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v\n", release)
}

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module github.com/irlndts/go-discogs
go 1.12

0
go.sum Normal file
View File

View File

@@ -1,57 +0,0 @@
package discogs
import (
"github.com/irlndts/go-apirequest"
"net/http"
)
type LabelService struct {
api *apirequest.API
}
type LabelParams struct {
Label_id string
Page int
Per_page int
}
type Label struct {
Profile string `json:"profile"`
Releases_url string `json:"releases_url"`
Name string `json:"name"`
Contact_info string `json:"contact_info"`
Uri string `json:"uri"`
Sublabels []Sublable `json:"sublabels"`
Urls []string `json:"urls"`
Images []Image `json:"images"`
Resource_url string `json:"resource_url"`
Id int `json:"id"`
Data_quality string `json:"data_quality"`
}
type LabelReleases struct {
Pagination Page `json:"pagination"`
Releases []ReleaseSource `json:"releases"`
}
func newLabelService(api *apirequest.API) *LabelService {
return &LabelService{
api: api.Path("labels/"),
}
}
func (self *LabelService) Label(params *LabelParams) (*Label, *http.Response, error) {
label := new(Label)
apiError := new(APIError)
resp, err := self.api.New().Get(params.Label_id).Receive(label, apiError)
return label, resp, relevantError(err, *apiError)
}
func (self *LabelService) Releases(params *LabelParams) (*LabelReleases, *http.Response, error) {
releases := new(LabelReleases)
apiError := new(APIError)
resp, err := self.api.New().Get(params.Label_id+"/releases").QueryStruct(params).Receive(releases, apiError)
return releases, resp, relevantError(err, *apiError)
}

View File

@@ -1,26 +0,0 @@
package discogs
import (
"fmt"
"testing"
)
func TestLabelService_Label(t *testing.T) {
expectedId := 1000
d := NewClient().UserAgent("UnitTestClient/0.0.1 +https://github.com/irlndts/go-discogs")
label, _, err := d.Label.Label(&LabelParams{Label_id: "1000"})
check(t, err)
assert(t, label.Id == expectedId, fmt.Sprintf("Release.Title looked for %s, and received %s ", expectedId, label.Id))
}
func TestLabelService_Releases(t *testing.T) {
expectedId := "Good Time"
d := NewClient().UserAgent("UnitTestClient/0.0.1 +https://github.com/irlndts/go-discogs")
label, _, err := d.Label.Releases(&LabelParams{Label_id: "1000"})
check(t, err)
assert(t, label.Releases[0].Title == expectedId, fmt.Sprintf("Release.Title looked for %s, and received %s ", expectedId, label.Releases[0].Title))
}

View File

@@ -1,63 +0,0 @@
package discogs
import (
"github.com/irlndts/go-apirequest"
"net/http"
)
type MasterService struct {
api *apirequest.API
}
type MasterParams struct {
Master_id string
}
type MasterVersionParams struct {
Master_id string
Page int
Per_page int
}
type Master struct {
Styles []string `json:"styles"`
Genres []string `json:"genres"`
Videos []Video `json:"videos"`
Title string `json:"title"`
Main_release int `json:"main_release"`
Main_release_url string `json:"main_release_url"`
Uri string `json:"uri"`
Artists []Artist `json:"artists"`
Version_url string `json:"version_url"`
Year int `json:"year"`
Images []Image `json:"images"`
Resource_url string `json:"resource_url"`
Tracklist []Track `json:"tracklist"`
Id int `json:"id"`
Data_quality string `json:"data_quality"`
}
type MasterVersions struct {
Pagination Page `json:"pagination"`
Versions []Version `json:"versions"`
}
func newMasterService(api *apirequest.API) *MasterService {
return &MasterService{
api: api.Path("masters/"),
}
}
func (self *MasterService) Master(params *MasterParams) (*Master, *http.Response, error) {
master := new(Master)
apiError := new(APIError)
resp, err := self.api.New().Get(params.Master_id).Receive(master, apiError)
return master, resp, relevantError(err, *apiError)
}
func (self *MasterService) Versions(params *MasterVersionParams) (*MasterVersions, *http.Response, error) {
versions := new(MasterVersions)
apiError := new(APIError)
resp, err := self.api.New().Get(params.Master_id+"/versions").QueryStruct(params).Receive(versions, apiError)
return versions, resp, relevantError(err, *apiError)
}

View File

@@ -1,26 +0,0 @@
package discogs
import (
"fmt"
"testing"
)
func TestMasterService_Master(t *testing.T) {
expectedTitle := "Elephant Riddim"
d := NewClient().UserAgent("UnitTestClient/0.0.1 +https://github.com/irlndts/go-discogs")
master, _, err := d.Master.Master(&MasterParams{Master_id: "960657"})
check(t, err)
assert(t, master.Title == expectedTitle, fmt.Sprintf("master.Title looked for %s, and received %s ", expectedTitle, master.Title))
}
func TestMasterService_Versions(t *testing.T) {
expectedTitle := "Stardiver"
d := NewClient().UserAgent("UnitTestClient/0.0.1 +https://github.com/irlndts/go-discogs")
versions, _, err := d.Master.Versions(&MasterVersionParams{Master_id: "1000", Page: 1, Per_page: 1})
check(t, err)
assert(t, versions.Versions[0].Title == expectedTitle, fmt.Sprintf("master.Title looked for %s, and received %s ", expectedTitle, versions.Versions[0].Title))
}

189
models.go Normal file
View File

@@ -0,0 +1,189 @@
package discogs
import (
"net/url"
"strconv"
)
// Video ...
type Video struct {
Description string `json:"description"`
Duration int `json:"duration"`
Embed bool `json:"embed"`
Title string `json:"title"`
URI string `json:"uri"`
}
// ArtistSource ...
type ArtistSource struct {
Anv string `json:"anv"`
ID int `json:"id"`
Join string `json:"join"`
Name string `json:"name"`
ResourceURL string `json:"resource_url"`
Role string `json:"role"`
Tracks string `json:"tracks"`
}
// Image ...
type Image struct {
Height int `json:"height"`
Width int `json:"width"`
ResourceURL string `json:"resource_url"`
Type string `json:"type"`
URI string `json:"uri"`
URI150 string `json:"uri150"`
}
// Track ...
type Track struct {
Duration string `json:"duration"`
Position string `json:"position"`
Title string `json:"title"`
Type string `json:"type_"`
Extraartists []ArtistSource `json:"extraartists"`
Artists []ArtistSource `json:"artists"`
}
// LabelSource ...
type LabelSource struct {
Catno string `json:"catno"`
EntityType string `json:"entity_type"`
ID int `json:"id"`
Name string `json:"name"`
ResourceURL string `json:"resource_url"`
}
// Identifier ...
type Identifier struct {
Type string `json:"type"`
Value string `json:"value"`
}
// Format ...
type Format struct {
Descriptions []string `json:"descriptions"`
Name string `json:"name"`
Qty string `json:"qty"`
}
// Company ...
type Company struct {
Catno string `json:"catno"`
EntityType string `json:"entity_type"`
EntityTypeName string `json:"entity_type_name"`
ID int `json:"id"`
Name string `json:"name"`
ResourceURL string `json:"resource_url"`
}
// Community ...
type Community struct {
Contributors []Contributor `json:"contributors"`
DataQuality string `json:"string"`
Have int `json:"have"`
Rating Rating `json:"rating"`
Status string `json:"status"`
Submitter Submitter `json:"submitter"`
Want int `json:"want"`
}
// Submitter ...
type Submitter struct {
ResourceURL string `json:"resource_url"`
Username string `json:"username"`
}
// Rating ...
type Rating struct {
Average float32 `json:"average"`
Count int `json:"count"`
}
// Contributor ...
type Contributor struct {
ResourceURL string `json:"resource_url"`
Username string `json:"username"`
}
// Page ...
type Page struct {
PerPage int `json:"per_page"`
Items int `json:"items"`
Page int `json:"page"`
URLs URLsList `json:"urls"`
Pages int `json:"pages"`
}
// URLsList ...
type URLsList struct {
Last string `json:"last"`
Next string `json:"next"`
}
// Version ...
type Version struct {
Catno string `json:"catno"`
Country string `json:"country"`
Format string `json:"format"`
ID int `json:"id"`
Label string `json:"label"`
Released string `json:"released"`
ResourceURL string `json:"resource_url"`
Status string `json:"status"`
Thumb string `json:"thumb"`
Title string `json:"title"`
}
// Member ...
type Member struct {
Active bool `json:"active"`
ID int `json:"id"`
Name string `json:"name"`
ResourceURL string `json:"resource_url"`
}
// Sublable ...
type Sublable struct {
ResourceURL string `json:"url"`
ID int `json:"id"`
Name string `json:"name"`
}
// ReleaseSource ...
type ReleaseSource struct {
Artist string `json:"artist"`
Catno string `json:"catno"`
Format string `json:"format"`
ID int `json:"id"`
ResourceURL string `json:"resource_url"`
Status string `json:"status"`
Thumb string `json:"thumb"`
Title string `json:"title"`
Year int `json:"year"`
MainRelease int `json:"main_release"`
Role string `json:"role"`
Type string `json:"type"`
}
// Pagination ...
type Pagination struct {
Sort string // year, title, format
SortOrder string // asc, desc
Page int
PerPage int
}
// toParams converts pagaination params to request values
func (p *Pagination) params() url.Values {
if p == nil {
return nil
}
params := url.Values{}
params.Set("sort", p.Sort)
params.Set("sort_order", p.SortOrder)
params.Set("page", strconv.Itoa(p.Page))
params.Set("per_page", strconv.Itoa(p.PerPage))
return params
}

View File

@@ -1,61 +0,0 @@
package discogs
import (
"github.com/irlndts/go-apirequest"
"net/http"
)
type ReleaseService struct {
api *apirequest.API
}
type ReleaseParams struct {
Release_id string
}
type Release struct {
Title string `json:"title"`
Id int `json:"id"`
Artists []ArtistSource `json:"artists"`
Data_quality string `json:"data_quality"`
Thumb string `json:"thumb"`
Community Community `json:"community"`
Companies []Company `json:"companies"`
Country string `json:"country"`
Date_added string `json:"date_added"`
Date_changed string `json:"date_changed"`
Estimated_weight int `json:"estimated_weight"`
Extraartists []ArtistSource `json:"extraartists"`
Format_quantity int `json:"format_quantity"`
Formats []Format `json:"formats"`
Genres []string `json:"genres"`
Identifiers []Identifier `json:"identifiers"`
Images []Image `json:"images"`
Labels []LabelSource `json:"labels"`
Master_id int `json:"master_id"`
Master_url string `json:"master_url"`
Notes string `josn:"notes"`
Released string `json:"released"`
Released_formatted string `json:"released_formatted"`
Resource_url string `json:"resource_url"`
Status string `json:"status"`
Styles []string `json:"styles"`
Tracklist []Track `json:"tracklist"`
Uri string `json:"uri"`
Videos []Video `json:"videos"`
Year int `json:"year"`
}
func newReleaseService(api *apirequest.API) *ReleaseService {
return &ReleaseService{
api: api.Path("releases/"),
}
}
func (self *ReleaseService) Release(params *ReleaseParams) (*Release, *http.Response, error) {
release := new(Release)
apiError := new(APIError)
resp, err := self.api.New().Get(params.Release_id).Receive(release, apiError)
return release, resp, relevantError(err, *apiError)
}

View File

@@ -1,16 +0,0 @@
package discogs
import (
"fmt"
"testing"
)
func TestReleaseService_Release(t *testing.T) {
expectedTitle := "Elephant Riddim"
d := NewClient().UserAgent("UnitTestClient/0.0.1 +https://github.com/irlndts/go-discogs")
release, _, err := d.Release.Release(&ReleaseParams{Release_id: "8138518"})
check(t, err)
assert(t, release.Title == expectedTitle, fmt.Sprintf("Release.Title looked for %s, and received %s ", expectedTitle, release.Title))
}

146
search.go Normal file
View File

@@ -0,0 +1,146 @@
package discogs
import (
"net/url"
"strconv"
)
// SearchService ...
type SearchService struct {
url string
}
func newSearchService(url string) *SearchService {
return &SearchService{
url: url,
}
}
// SearchRequest describes search request
type SearchRequest struct {
Q string // search query
Type string // one of release, master, artist, label
Title string // search by combined “Artist Name - Release Title” title field
ReleaseTitle string // search release titles
Credit string // search release credits
Artist string // search artist names
Anv string // search artist ANV
Label string // search label names
Genre string // search genres
Style string // search styles
Country string // search release country
Year string // search release year
Format string // search formats
Catno string // search catalog number
Barcode string // search barcodes
Track string // search track titles
Submitter string // search submitter username
Contributor string // search contributor usernames
Page int
PerPage int
}
func (r *SearchRequest) params() url.Values {
if r == nil {
return nil
}
params := url.Values{}
if r.Q != "" {
params.Set("q", r.Q)
}
if r.Type != "" {
params.Set("type", r.Type)
}
if r.Title != "" {
params.Set("title", r.Title)
}
if r.ReleaseTitle != "" {
params.Set("release_title", r.ReleaseTitle)
}
if r.Credit != "" {
params.Set("credit", r.Credit)
}
if r.Artist != "" {
params.Set("artist", r.Artist)
}
if r.Anv != "" {
params.Set("anv", r.Anv)
}
if r.Label != "" {
params.Set("label", r.Label)
}
if r.Genre != "" {
params.Set("genre", r.Genre)
}
if r.Style != "" {
params.Set("style", r.Style)
}
if r.Country != "" {
params.Set("country", r.Country)
}
if r.Year != "" {
params.Set("year", r.Year)
}
if r.Format != "" {
params.Set("format", r.Format)
}
if r.Catno != "" {
params.Set("catno", r.Catno)
}
if r.Barcode != "" {
params.Set("barcode", r.Barcode)
}
if r.Track != "" {
params.Set("track", r.Track)
}
if r.Submitter != "" {
params.Set("submitter", r.Submitter)
}
if r.Contributor != "" {
params.Set("contributor", r.Contributor)
}
params.Set("page", strconv.Itoa(r.Page))
if r.PerPage != 0 {
params.Set("per_page", strconv.Itoa(r.PerPage))
}
return params
}
// Search describes search response
type Search struct {
Pagination Page `json:"pagination"`
Results []Result `json:"results,omitempty"`
}
// Result describes a part of search result
type Result struct {
Style []string `json:"style,omitempty"`
Thumb string `json:"thumb,omitempty"`
CoverImage string `json:"cover_image,omitempty"`
Title string `json:"title,omitempty"`
Country string `json:"country,omitempty"`
Format []string `json:"format,omitempty"`
URI string `json:"uri,omitempty"`
Community Community `json:"community,omitempty"`
Label []string `json:"label,omitempty"`
Catno string `json:"catno,omitempty"`
Year string `json:"year,omitempty"`
Genre []string `json:"genre,omitempty"`
ResourceURL string `json:"resource_url,omitempty"`
Type string `json:"type,omitempty"`
ID int `json:"id,omitempty"`
}
// Search makes search request to discogs.
// Issue a search query to our database. This endpoint accepts pagination parameters.
// Authentication (as any user) is required.
// https://www.discogs.com/developers/#page:database,header:database-search
func (s *SearchService) Search(req SearchRequest) (*Search, error) {
var search *Search
err := request(s.url, req.params(), &search)
return search, err
}