37 Commits
0.0.3 ... v0.1

Author SHA1 Message Date
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
19 changed files with 1178 additions and 426 deletions

View File

@@ -2,13 +2,6 @@
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.
### Changelog
```
14.02.2017
- search is implemented
- minor improvements
```
### Feauteres
* Database
* [Releases](#releases)
@@ -22,7 +15,7 @@ go-discogs is a Go client library for the [Discogs API](https://www.discogs.com/
Install
--------
go get github.com/irlndts/go-discogs
go get -u github.com/irlndts/go-discogs
Usage
---------
@@ -31,49 +24,35 @@ First of all import library and init client variable. According to discogs api d
```go
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 := discogs.NewClient("TestDiscogsClient/0.0.1 +example.com", "")
```
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. Key-secret doesn't implemented yet, but token is yes.
```go
client := discogs.NewClient("TestDiscogsClient/0.0.1 +example.com", "sometoken")
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 := client.Release.Release(params)
fmt.Println(fmt.Println(release.Artists[0].Name, " - ", release.Title)) // St. Petersburg Ska-Jazz Review - Elephant Riddim
```
#### Artists
```go
params := &discogs.LabelParams{Label_id: "890477", Page: 2, Per_page: 3}
label, _, err := client.Label.Releases(params)
for _, release := range label.Releases {
fmt.Println(release.Title)
}
/*
Someday / I Hate Everything About You
Spy Potion
Surf Attack From Russia
*/
release, _ := client.Release.Release(9893847)
fmt.Println(release.Artists[0].Name, " - ", release.Title)
// St. Petersburg Ska-Jazz Review - Elephant Riddim
```
#### Search
Issue a search query to discogs database. This endpoint accepts pagination parameters
Issue a search query to discogs database. This endpoint accepts pagination parameters.
Authentication (as any user) is required.
Use `SearchRequest` struc to create a request
Use `SearchRequest` struct to create a request.
```go
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)
Release_title string // search release titles (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)
@@ -90,14 +69,14 @@ type SearchRequest struct {
Contributer string // search contributor usernames (optional)
Page int // optional
Per_page int // optional
PerPage int // optional
}
```
Example
```go
request:= &discogs.SearchRequest{Artist: "reggaenauts", Release_title: "river rock", Page: 0, Per_page: 1}
search, _, err := client.Search(request)
request := discogs.SearchRequest{Artist: "reggaenauts", ReleaseTitle: "river rock", Page: 0, PerPage: 1}
search, _ := client.Search(request)
for _, r := range search.Results {
fmt.Println(r.Title)
@@ -105,4 +84,3 @@ Example
```
etc.
More examples - soon

View File

@@ -1,22 +1,19 @@
package discogs
import (
"github.com/irlndts/go-apirequest"
"net/http"
)
import "strconv"
// ArtistService ...
type ArtistService struct {
api *apirequest.API
url string
}
type ArtistParams struct {
Artist_id string
Sort string // year, title, format
Sort_order string // asc, desc
Page int
Per_page int
func newArtistService(url string) *ArtistService {
return &ArtistService{
url: url,
}
}
// Artist ...
type Artist struct {
Namevariations []string `json:"namevariations"`
Profile string `json:"profile"`
@@ -30,29 +27,26 @@ type Artist struct {
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/"),
// Artist represents a person in the discogs database
func (s *ArtistService) Artist(artistID int) (*Artist, error) {
var artist *Artist
if err := request(s.url+strconv.Itoa(artistID), nil, &artist); err != nil {
return nil, err
}
return artist, nil
}
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)
// ArtistReleases ...
type ArtistReleases struct {
Pagination Page `json:"pagination"`
Releases []ReleaseSource `json:"releases"`
}
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)
// Releases returns a list of releases and masters associated with the artist.
func (s *ArtistService) Releases(artistID int, pagination *Pagination) (*ArtistReleases, error) {
var releases *ArtistReleases
if err := request(s.url+strconv.Itoa(artistID)+"/releases", pagination.toParams(), &releases); err != nil {
return nil, err
}
return releases, nil
}

View File

@@ -1,26 +0,0 @@
package discogs
import (
"fmt"
"testing"
)
func TestArtistService_Artist(t *testing.T) {
expectedId := 1000
d := NewClient(testUserAgent, testToken)
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(testUserAgent, testToken)
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,18 +1,26 @@
package discogs
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/irlndts/go-apirequest"
"net/url"
)
const (
discogsAPI = "https://api.discogs.com/"
)
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
@@ -20,29 +28,74 @@ type Client struct {
Search *SearchService
}
var header *http.Header
// NewClient returns a new Client.
func NewClient(useragent, token string) *Client {
base := apirequest.New().Client(&http.Client{}).Base(discogsAPI).Add("User-Agent", useragent).Add("Authorization", "Discogs token="+token)
func NewClient(o *Options) (*Client, error) {
header = &http.Header{}
if o == nil || o.UserAgent == "" {
return nil, fmt.Errorf("failed to set user-agent")
}
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,
Artist: newArtistService(base.New()),
Label: newLabelService(base.New()),
Master: newMasterService(base.New()),
Release: newReleaseService(base.New()),
Search: newSearchService(base.New()),
Release: newReleaseService(o.URL+"releases/", cur),
Artist: newArtistService(o.URL + "artists/"),
Label: newLabelService(o.URL + "labels/"),
Master: newMasterService(o.URL + "masters/"),
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 "", fmt.Errorf("%v\n", "Invalid currency abbreviation.")
}
}
// UserAgent sets specified user agent
// Discogs required it
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 {
fmt.Println(path + "?" + params.Encode())
r, err := http.NewRequest("GET", path+"?"+params.Encode(), nil)
if err != nil {
return err
}
r.Header = *header
// Token sets tokens, it's required for some queries like search
func (c *Client) Token(token string) *Client {
c.api.Set("Authorization", "Discogs token="+token)
return c
client := &http.Client{}
response, err := client.Do(r)
if err != nil {
return err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
return json.Unmarshal(body, &resp)
}

View File

@@ -9,14 +9,23 @@ const (
testToken = ""
)
func check(t *testing.T, e error) {
if e != nil {
t.Error(e)
func initDiscogsClient(t *testing.T, options *Options) *Client {
if options == nil {
options = &Options{
UserAgent: testUserAgent,
Currency: "USD",
Token: testToken,
}
}
}
func assert(t *testing.T, condition bool, assertion string) {
if !condition {
t.Errorf("Assertion failed: %v", assertion)
if options.UserAgent == "" {
options.UserAgent = testUserAgent
}
client, err := NewClient(options)
if err != nil {
t.Fatalf("failed to create client: %s", err)
}
return client
}

View File

@@ -9,6 +9,7 @@ type APIError struct {
Message string `json:"message"`
}
// Error ...
func (e APIError) Error() string {
if e.Message != "" {
return fmt.Sprintf("discogs: %v", e.Message)

View File

@@ -7,17 +7,22 @@ import (
)
func main() {
d := discogs.NewClient("TestDiscogsClient/0.0.1 +http://irlndts.moscow", "")
request := &discogs.SearchRequest{Q: "The Reggaenauts - River Rock / Thursday Kick-off", Page: 0, Per_page: 1}
search, _, err := d.Search.Search(request)
d, err := discogs.NewClient(&discogs.Options{
UserAgent: "TestDiscogsClient/0.0.1 +http://example.com",
Currency: "EUR",
Token: "",
})
if err != nil {
fmt.Println(err)
return
}
for _, r := range search.Results {
fmt.Println(r.Id, r.Title)
release, err := d.Search.Search(discogs.SearchRequest{
Q: "Ska-Jazz Review",
})
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%+v\n", release)
}

View File

@@ -1,58 +1,56 @@
package discogs
import (
"net/http"
"github.com/irlndts/go-apirequest"
"strconv"
)
// LabelService ...
type LabelService struct {
api *apirequest.API
url string
}
type LabelParams struct {
Label_id string
Page int
Per_Page int
func newLabelService(url string) *LabelService {
return &LabelService{
url: url,
}
}
// Label resource represents a label, company, recording studio, location,
// or other entity involved with artists and releases.
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"`
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 *LabelService) Label(labelID int) (*Label, error) {
var label *Label
if err := request(s.url+strconv.Itoa(labelID), nil, &label); err != nil {
return nil, err
}
return label, nil
}
// LabelReleases is a list of Releases associated with the label.
type LabelReleases struct {
Pagination Page `json:"pagination"`
Releases []ReleaseSource `json:"releases"`
}
func newLabelService(api *apirequest.API) *LabelService {
return &LabelService{
api: api.Path("labels/"),
// Releases returns a list of Releases associated with the label.
func (s *LabelService) Releases(labelID int, pagination *Pagination) (*LabelReleases, error) {
var releases *LabelReleases
if err := request(s.url+strconv.Itoa(labelID)+"/releases", pagination.toParams(), &releases); err != nil {
return nil, err
}
}
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)
return releases, nil
}

View File

@@ -1,26 +0,0 @@
package discogs
import (
"fmt"
"testing"
)
func TestLabelService_Label(t *testing.T) {
expectedId := 1000
d := NewClient(testUserAgent, testToken)
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 := "Cha Cha Twist"
d := NewClient(testUserAgent, testToken)
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,24 +1,21 @@
package discogs
import (
"github.com/irlndts/go-apirequest"
"net/http"
"strconv"
)
type MasterService struct {
api *apirequest.API
url string
}
type MasterParams struct {
Master_id string
}
type MasterVersionParams struct {
Master_id string
Page int
Per_page int
func newMasterService(url string) *MasterService {
return &MasterService{
url: url,
}
}
// 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"`
@@ -37,27 +34,26 @@ type Master struct {
Data_quality string `json:"data_quality"`
}
// Master returns a master release
func (s *MasterService) Master(masterID int) (*Master, error) {
var master *Master
if err := request(s.url+strconv.Itoa(masterID), nil, &master); err != nil {
return nil, err
}
return master, nil
}
// MasterVersions retrieves a list of all releases that are versions of this master.
type MasterVersions struct {
Pagination Page `json:"pagination"`
Versions []Version `json:"versions"`
}
func newMasterService(api *apirequest.API) *MasterService {
return &MasterService{
api: api.Path("masters/"),
// Versions retrieves a list of all Releases that are versions of this master
func (s *MasterService) Versions(masterID int, pagination *Pagination) (*MasterVersions, error) {
var versions *MasterVersions
if err := request(s.url+strconv.Itoa(masterID)+"/versions", pagination.toParams(), &versions); err != nil {
return nil, err
}
}
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)
return versions, nil
}

View File

@@ -1,26 +0,0 @@
package discogs
import (
"fmt"
"testing"
)
func TestMasterService_Master(t *testing.T) {
expectedTitle := "Elephant Riddim"
d := NewClient(testUserAgent, testToken)
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(testUserAgent, testToken)
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))
}

183
models.go
View File

@@ -1,32 +1,41 @@
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"`
URI string `json:"uri"`
}
// ArtistSource ...
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"`
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"`
Resource_url string `json:"resource_url"`
Type string `json:"type"`
Uri string `json:"uri"`
Uri150 string `json:"uri150"`
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"`
@@ -35,37 +44,42 @@ type Track struct {
Extraartists []ArtistSource `json:"extraartists"`
}
// LabelSource ...
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"`
Catno string `json:"catno"`
EntityType string `json:"entity_type"`
ID int `json:"id"`
Name string `json:"name"`
ResourceURL string `json:"resource_url"`
}
// Itentifier ...
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"`
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"`
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"`
Data_quality string `json:"string"`
DataQuality string `json:"string"`
Have int `json:"have"`
Rating Rating `json:"rating"`
Status string `json:"status"`
@@ -73,71 +87,110 @@ type Community struct {
Want int `json:"want"`
}
// Submitter ...
type Submitter struct {
Resource_url string `json:"resource_url"`
Username string `json:"username"`
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 {
Resource_url string `json:"resource_url"`
Username string `json:"username"`
ResourceURL string `json:"resource_url"`
Username string `json:"username"`
}
// Page ...
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"`
PerPage int `json:"per_page"`
Items int `json:"items"`
Page int `json:"page"`
URLs URLsList `json:"urls"`
Pages int `json:"pages"`
}
type URLS struct {
// 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"`
Resource_url string `json:"resource_url"`
Status string `json:"status"`
Thumb string `json:"thumb"`
Title string `json:"title"`
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"`
Resource_url string `json:"resource_url"`
Active bool `json:"active"`
ID int `json:"id"`
Name string `json:"name"`
ResourceURL string `json:"resource_url"`
}
// Sublable ...
type Sublable struct {
Resource_url string `json:"url"`
Id int `json:"id"`
Name string `json:"name"`
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"`
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"`
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) toParams() url.Values {
if p == nil {
return nil
}
params := url.Values{}
if p.Sort != "" {
params.Set("sort", p.Sort)
}
if p.SortOrder != "" {
params.Set("sort_order", p.SortOrder)
}
if p.Page != 0 {
params.Set("page", strconv.Itoa(p.Page))
}
if p.PerPage != 0 {
params.Set("per_page", strconv.Itoa(p.PerPage))
}
return params
}

View File

@@ -1,61 +1,90 @@
package discogs
import (
"github.com/irlndts/go-apirequest"
"net/http"
"net/url"
"strconv"
)
// ReleaseService ...
type ReleaseService struct {
api *apirequest.API
url string
currency string
}
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 {
func newReleaseService(url string, currency string) *ReleaseService {
return &ReleaseService{
api: api.Path("releases/"),
url: url,
currency: currency,
}
}
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)
// ReqRelease serves release request
type ReqRelease struct {
CurrAbbr string
}
// 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 *ReleaseService) Release(releaseID int) (*Release, error) {
params := url.Values{}
params.Set("CurrAbbr", s.currency)
var release *Release
if err := request(s.url+strconv.Itoa(releaseID), params, &release); err != nil {
return nil, err
}
return release, nil
}
// ReleaseRating serves response for community release rating request
type ReleaseRating struct {
ID int `json:"release_id"`
Rating Rating `json:"rating"`
}
// Ratings retruns community release rating
func (s *ReleaseService) Rating(releaseID int) (*ReleaseRating, error) {
var rating *ReleaseRating
if err := request(s.url+strconv.Itoa(releaseID)+"/rating", nil, &rating); err != nil {
return nil, err
}
return rating, nil
}

View File

@@ -1,16 +1,30 @@
package discogs
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
)
func TestReleaseService_Release(t *testing.T) {
func ReleaseServer(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
io.WriteString(w, `{"title":"Elephant Riddim"}`)
}
func TestReleaseServiceRelease(t *testing.T) {
expectedTitle := "Elephant Riddim"
d := NewClient(testUserAgent, testToken)
release, _, err := d.Release.Release(&ReleaseParams{Release_id: "8138518"})
ts := httptest.NewServer(http.HandlerFunc(ReleaseServer))
defer ts.Close()
check(t, err)
assert(t, release.Title == expectedTitle, fmt.Sprintf("Release.Title looked for %s, and received %s ", expectedTitle, release.Title))
d := initDiscogsClient(t, &Options{URL: ts.URL})
release, err := d.Release.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)
}
}

126
search.go
View File

@@ -1,71 +1,83 @@
package discogs
import (
"net/http"
"github.com/irlndts/go-apirequest"
"github.com/google/go-querystring/query"
)
// SearchService ...
type SearchService struct {
api *apirequest.API
url string
}
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)
Release_title 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)
Page int // optional
Per_page int // optional
}
type Search struct {
Pagination Page `json:"pagination"`
Results []Result `json:"results"`
}
type Result struct {
Style []string `json:"style"`
Thumb string `json:"thumb"`
Title string `json:"title"`
Country string `json:"country"`
Format []string `json:"format"`
Uri string `json:"uri"`
Community Community `json:"community"`
Label []string `json:"label"`
Catno string `json:"catno"`
Year string `json:"year"`
Genre []string `json:"genre"`
Resource_url string `json:"resource_url"`
Type string `json:"type"`
Id int `json:"id"`
}
func newSearchService(api *apirequest.API) *SearchService {
func newSearchService(url string) *SearchService {
return &SearchService{
api: api.Path("database/search"),
url: url,
}
}
func (self *SearchService) Search(params *SearchRequest) (*Search, *http.Response, error) {
search := new(Search)
apiError := new(APIError)
// SerachRequest describes search request
type SearchRequest struct {
Q string `url:"q,omitempty"` // search query
Type string `url:"type,omitempty"` // one of release, master, artist, label
Title string `url:"title,omitempty"` // search by combined “Artist Name - Release Title” title field
ReleaseTitle string `url:"release_title,omitempty"` // search release titles
Credit string `url:"credit,omitempty"` // search release credits
Artist string `url:"artist,omitempty"` // search artist names
Anv string `url:"anv,omitempty"` // search artist ANV
Label string `url:"label,omitempty"` // search label names
Genre string `url:"genre,omitempty"` // search genres
Style string `url:"style,omitempty"` // search styles
Country string `url:"country,omitempty"` // search release country
Year string `url:"year,omitempty"` // search release year
Format string `url:"format,omitempty"` // search formats
Catno string `url:"catno,omitempty"` // search catalog number
Barcode string `url:"barcode,omitempty"` // search barcodes
Track string `url:"track,omitempty"` // search track titles
Submitter string `url:"submitter,omitempty"` // search submitter username
Contributer string `url:"contributer,omitempty"` // search contributor usernames
resp, err := self.api.New().QueryStruct(params).Receive(search, apiError)
return search, resp, relevantError(err, *apiError)
Page int `url:"page,omitempty"`
PerPage int `url:"per_page,omitempty"`
}
// 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"`
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
// TODO(irlndts): improve params to pass
func (s *SearchService) Search(req SearchRequest) (*Search, error) {
params, err := query.Values(req)
if err != nil {
return nil, err
}
var search *Search
if err := request(s.url, params, &search); err != nil {
return nil, err
}
return search, nil
}

27
vendor/github.com/google/go-querystring/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2013 Google. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

320
vendor/github.com/google/go-querystring/query/encode.go generated vendored Normal file
View File

@@ -0,0 +1,320 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package query implements encoding of structs into URL query parameters.
//
// As a simple example:
//
// type Options struct {
// Query string `url:"q"`
// ShowAll bool `url:"all"`
// Page int `url:"page"`
// }
//
// opt := Options{ "foo", true, 2 }
// v, _ := query.Values(opt)
// fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2"
//
// The exact mapping between Go values and url.Values is described in the
// documentation for the Values() function.
package query
import (
"bytes"
"fmt"
"net/url"
"reflect"
"strconv"
"strings"
"time"
)
var timeType = reflect.TypeOf(time.Time{})
var encoderType = reflect.TypeOf(new(Encoder)).Elem()
// Encoder is an interface implemented by any type that wishes to encode
// itself into URL values in a non-standard way.
type Encoder interface {
EncodeValues(key string, v *url.Values) error
}
// Values returns the url.Values encoding of v.
//
// Values expects to be passed a struct, and traverses it recursively using the
// following encoding rules.
//
// Each exported struct field is encoded as a URL parameter unless
//
// - the field's tag is "-", or
// - the field is empty and its tag specifies the "omitempty" option
//
// The empty values are false, 0, any nil pointer or interface value, any array
// slice, map, or string of length zero, and any time.Time that returns true
// for IsZero().
//
// The URL parameter name defaults to the struct field name but can be
// specified in the struct field's tag value. The "url" key in the struct
// field's tag value is the key name, followed by an optional comma and
// options. For example:
//
// // Field is ignored by this package.
// Field int `url:"-"`
//
// // Field appears as URL parameter "myName".
// Field int `url:"myName"`
//
// // Field appears as URL parameter "myName" and the field is omitted if
// // its value is empty
// Field int `url:"myName,omitempty"`
//
// // Field appears as URL parameter "Field" (the default), but the field
// // is skipped if empty. Note the leading comma.
// Field int `url:",omitempty"`
//
// For encoding individual field values, the following type-dependent rules
// apply:
//
// Boolean values default to encoding as the strings "true" or "false".
// Including the "int" option signals that the field should be encoded as the
// strings "1" or "0".
//
// time.Time values default to encoding as RFC3339 timestamps. Including the
// "unix" option signals that the field should be encoded as a Unix time (see
// time.Unix())
//
// Slice and Array values default to encoding as multiple URL values of the
// same name. Including the "comma" option signals that the field should be
// encoded as a single comma-delimited value. Including the "space" option
// similarly encodes the value as a single space-delimited string. Including
// the "semicolon" option will encode the value as a semicolon-delimited string.
// Including the "brackets" option signals that the multiple URL values should
// have "[]" appended to the value name. "numbered" will append a number to
// the end of each incidence of the value name, example:
// name0=value0&name1=value1, etc.
//
// Anonymous struct fields are usually encoded as if their inner exported
// fields were fields in the outer struct, subject to the standard Go
// visibility rules. An anonymous struct field with a name given in its URL
// tag is treated as having that name, rather than being anonymous.
//
// Non-nil pointer values are encoded as the value pointed to.
//
// Nested structs are encoded including parent fields in value names for
// scoping. e.g:
//
// "user[name]=acme&user[addr][postcode]=1234&user[addr][city]=SFO"
//
// All other values are encoded using their default string representation.
//
// Multiple fields that encode to the same URL parameter name will be included
// as multiple URL values of the same name.
func Values(v interface{}) (url.Values, error) {
values := make(url.Values)
val := reflect.ValueOf(v)
for val.Kind() == reflect.Ptr {
if val.IsNil() {
return values, nil
}
val = val.Elem()
}
if v == nil {
return values, nil
}
if val.Kind() != reflect.Struct {
return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind())
}
err := reflectValue(values, val, "")
return values, err
}
// reflectValue populates the values parameter from the struct fields in val.
// Embedded structs are followed recursively (using the rules defined in the
// Values function documentation) breadth-first.
func reflectValue(values url.Values, val reflect.Value, scope string) error {
var embedded []reflect.Value
typ := val.Type()
for i := 0; i < typ.NumField(); i++ {
sf := typ.Field(i)
if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue
}
sv := val.Field(i)
tag := sf.Tag.Get("url")
if tag == "-" {
continue
}
name, opts := parseTag(tag)
if name == "" {
if sf.Anonymous && sv.Kind() == reflect.Struct {
// save embedded struct for later processing
embedded = append(embedded, sv)
continue
}
name = sf.Name
}
if scope != "" {
name = scope + "[" + name + "]"
}
if opts.Contains("omitempty") && isEmptyValue(sv) {
continue
}
if sv.Type().Implements(encoderType) {
if !reflect.Indirect(sv).IsValid() {
sv = reflect.New(sv.Type().Elem())
}
m := sv.Interface().(Encoder)
if err := m.EncodeValues(name, &values); err != nil {
return err
}
continue
}
if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array {
var del byte
if opts.Contains("comma") {
del = ','
} else if opts.Contains("space") {
del = ' '
} else if opts.Contains("semicolon") {
del = ';'
} else if opts.Contains("brackets") {
name = name + "[]"
}
if del != 0 {
s := new(bytes.Buffer)
first := true
for i := 0; i < sv.Len(); i++ {
if first {
first = false
} else {
s.WriteByte(del)
}
s.WriteString(valueString(sv.Index(i), opts))
}
values.Add(name, s.String())
} else {
for i := 0; i < sv.Len(); i++ {
k := name
if opts.Contains("numbered") {
k = fmt.Sprintf("%s%d", name, i)
}
values.Add(k, valueString(sv.Index(i), opts))
}
}
continue
}
for sv.Kind() == reflect.Ptr {
if sv.IsNil() {
break
}
sv = sv.Elem()
}
if sv.Type() == timeType {
values.Add(name, valueString(sv, opts))
continue
}
if sv.Kind() == reflect.Struct {
reflectValue(values, sv, name)
continue
}
values.Add(name, valueString(sv, opts))
}
for _, f := range embedded {
if err := reflectValue(values, f, scope); err != nil {
return err
}
}
return nil
}
// valueString returns the string representation of a value.
func valueString(v reflect.Value, opts tagOptions) string {
for v.Kind() == reflect.Ptr {
if v.IsNil() {
return ""
}
v = v.Elem()
}
if v.Kind() == reflect.Bool && opts.Contains("int") {
if v.Bool() {
return "1"
}
return "0"
}
if v.Type() == timeType {
t := v.Interface().(time.Time)
if opts.Contains("unix") {
return strconv.FormatInt(t.Unix(), 10)
}
return t.Format(time.RFC3339)
}
return fmt.Sprint(v.Interface())
}
// isEmptyValue checks if a value should be considered empty for the purposes
// of omitting fields with the "omitempty" option.
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
}
if v.Type() == timeType {
return v.Interface().(time.Time).IsZero()
}
return false
}
// tagOptions is the string following a comma in a struct field's "url" tag, or
// the empty string. It does not include the leading comma.
type tagOptions []string
// parseTag splits a struct field's url tag into its name and comma-separated
// options.
func parseTag(tag string) (string, tagOptions) {
s := strings.Split(tag, ",")
return s[0], s[1:]
}
// Contains checks whether the tagOptions contains the specified option.
func (o tagOptions) Contains(option string) bool {
for _, s := range o {
if s == option {
return true
}
}
return false
}

View File

@@ -0,0 +1,328 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package query
import (
"fmt"
"net/url"
"reflect"
"testing"
"time"
)
type Nested struct {
A SubNested `url:"a"`
B *SubNested `url:"b"`
Ptr *SubNested `url:"ptr,omitempty"`
}
type SubNested struct {
Value string `url:"value"`
}
func TestValues_types(t *testing.T) {
str := "string"
strPtr := &str
timeVal := time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)
tests := []struct {
in interface{}
want url.Values
}{
{
// basic primitives
struct {
A string
B int
C uint
D float32
E bool
}{},
url.Values{
"A": {""},
"B": {"0"},
"C": {"0"},
"D": {"0"},
"E": {"false"},
},
},
{
// pointers
struct {
A *string
B *int
C **string
D *time.Time
}{
A: strPtr,
C: &strPtr,
D: &timeVal,
},
url.Values{
"A": {str},
"B": {""},
"C": {str},
"D": {"2000-01-01T12:34:56Z"},
},
},
{
// slices and arrays
struct {
A []string
B []string `url:",comma"`
C []string `url:",space"`
D [2]string
E [2]string `url:",comma"`
F [2]string `url:",space"`
G []*string `url:",space"`
H []bool `url:",int,space"`
I []string `url:",brackets"`
J []string `url:",semicolon"`
K []string `url:",numbered"`
}{
A: []string{"a", "b"},
B: []string{"a", "b"},
C: []string{"a", "b"},
D: [2]string{"a", "b"},
E: [2]string{"a", "b"},
F: [2]string{"a", "b"},
G: []*string{&str, &str},
H: []bool{true, false},
I: []string{"a", "b"},
J: []string{"a", "b"},
K: []string{"a", "b"},
},
url.Values{
"A": {"a", "b"},
"B": {"a,b"},
"C": {"a b"},
"D": {"a", "b"},
"E": {"a,b"},
"F": {"a b"},
"G": {"string string"},
"H": {"1 0"},
"I[]": {"a", "b"},
"J": {"a;b"},
"K0": {"a"},
"K1": {"b"},
},
},
{
// other types
struct {
A time.Time
B time.Time `url:",unix"`
C bool `url:",int"`
D bool `url:",int"`
}{
A: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC),
B: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC),
C: true,
D: false,
},
url.Values{
"A": {"2000-01-01T12:34:56Z"},
"B": {"946730096"},
"C": {"1"},
"D": {"0"},
},
},
{
struct {
Nest Nested `url:"nest"`
}{
Nested{
A: SubNested{
Value: "that",
},
},
},
url.Values{
"nest[a][value]": {"that"},
"nest[b]": {""},
},
},
{
struct {
Nest Nested `url:"nest"`
}{
Nested{
Ptr: &SubNested{
Value: "that",
},
},
},
url.Values{
"nest[a][value]": {""},
"nest[b]": {""},
"nest[ptr][value]": {"that"},
},
},
{
nil,
url.Values{},
},
}
for i, tt := range tests {
v, err := Values(tt.in)
if err != nil {
t.Errorf("%d. Values(%q) returned error: %v", i, tt.in, err)
}
if !reflect.DeepEqual(tt.want, v) {
t.Errorf("%d. Values(%q) returned %v, want %v", i, tt.in, v, tt.want)
}
}
}
func TestValues_omitEmpty(t *testing.T) {
str := ""
s := struct {
a string
A string
B string `url:",omitempty"`
C string `url:"-"`
D string `url:"omitempty"` // actually named omitempty, not an option
E *string `url:",omitempty"`
}{E: &str}
v, err := Values(s)
if err != nil {
t.Errorf("Values(%q) returned error: %v", s, err)
}
want := url.Values{
"A": {""},
"omitempty": {""},
"E": {""}, // E is included because the pointer is not empty, even though the string being pointed to is
}
if !reflect.DeepEqual(want, v) {
t.Errorf("Values(%q) returned %v, want %v", s, v, want)
}
}
type A struct {
B
}
type B struct {
C string
}
type D struct {
B
C string
}
type e struct {
B
C string
}
type F struct {
e
}
func TestValues_embeddedStructs(t *testing.T) {
tests := []struct {
in interface{}
want url.Values
}{
{
A{B{C: "foo"}},
url.Values{"C": {"foo"}},
},
{
D{B: B{C: "bar"}, C: "foo"},
url.Values{"C": {"foo", "bar"}},
},
{
F{e{B: B{C: "bar"}, C: "foo"}}, // With unexported embed
url.Values{"C": {"foo", "bar"}},
},
}
for i, tt := range tests {
v, err := Values(tt.in)
if err != nil {
t.Errorf("%d. Values(%q) returned error: %v", i, tt.in, err)
}
if !reflect.DeepEqual(tt.want, v) {
t.Errorf("%d. Values(%q) returned %v, want %v", i, tt.in, v, tt.want)
}
}
}
func TestValues_invalidInput(t *testing.T) {
_, err := Values("")
if err == nil {
t.Errorf("expected Values() to return an error on invalid input")
}
}
type EncodedArgs []string
func (m EncodedArgs) EncodeValues(key string, v *url.Values) error {
for i, arg := range m {
v.Set(fmt.Sprintf("%s.%d", key, i), arg)
}
return nil
}
func TestValues_Marshaler(t *testing.T) {
s := struct {
Args EncodedArgs `url:"arg"`
}{[]string{"a", "b", "c"}}
v, err := Values(s)
if err != nil {
t.Errorf("Values(%q) returned error: %v", s, err)
}
want := url.Values{
"arg.0": {"a"},
"arg.1": {"b"},
"arg.2": {"c"},
}
if !reflect.DeepEqual(want, v) {
t.Errorf("Values(%q) returned %v, want %v", s, v, want)
}
}
func TestValues_MarshalerWithNilPointer(t *testing.T) {
s := struct {
Args *EncodedArgs `url:"arg"`
}{}
v, err := Values(s)
if err != nil {
t.Errorf("Values(%q) returned error: %v", s, err)
}
want := url.Values{}
if !reflect.DeepEqual(want, v) {
t.Errorf("Values(%q) returned %v, want %v", s, v, want)
}
}
func TestTagParsing(t *testing.T) {
name, opts := parseTag("field,foobar,foo")
if name != "field" {
t.Fatalf("name = %q, want field", name)
}
for _, tt := range []struct {
opt string
want bool
}{
{"foobar", true},
{"foo", true},
{"bar", false},
{"field", false},
} {
if opts.Contains(tt.opt) != tt.want {
t.Errorf("Contains(%q) = %v", tt.opt, !tt.want)
}
}
}

13
vendor/vendor.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"comment": "",
"ignore": "",
"package": [
{
"checksumSHA1": "aDjb3mG2PnOgRgQXVXBxzPNc9I0=",
"path": "github.com/google/go-querystring/query",
"revision": "53e6ce116135b80d037921a7fdd5138cf32d7a8a",
"revisionTime": "2017-01-11T10:11:55Z"
}
],
"rootPath": "github.com/irlndts/go-discogs"
}