Merge pull request #28 from irlndts/RemoveVendor

This commit is contained in:
Artem Piskun 2019-06-06 15:24:00 +03:00 committed by GitHub
commit 3c48a3e6d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 93 additions and 436 deletions

View File

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

View File

@ -68,11 +68,8 @@ func (s *DatabaseService) Release(releaseID int) (*Release, error) {
params.Set("curr_abbr", s.currency) params.Set("curr_abbr", s.currency)
var release *Release var release *Release
if err := request(s.url+releasesURI+strconv.Itoa(releaseID), params, &release); err != nil { err := request(s.url+releasesURI+strconv.Itoa(releaseID), params, &release)
return nil, err return release, err
}
return release, nil
} }
// ReleaseRating serves response for community release rating request // ReleaseRating serves response for community release rating request
@ -84,11 +81,8 @@ type ReleaseRating struct {
// ReleaseRating retruns community release rating // ReleaseRating retruns community release rating
func (s *DatabaseService) ReleaseRating(releaseID int) (*ReleaseRating, error) { func (s *DatabaseService) ReleaseRating(releaseID int) (*ReleaseRating, error) {
var rating *ReleaseRating var rating *ReleaseRating
if err := request(s.url+releasesURI+strconv.Itoa(releaseID)+"/rating", nil, &rating); err != nil { err := request(s.url+releasesURI+strconv.Itoa(releaseID)+"/rating", nil, &rating)
return nil, err return rating, err
}
return rating, nil
} }
// Artist ... // Artist ...
@ -108,10 +102,8 @@ type Artist struct {
// Artist represents a person in the discogs database // Artist represents a person in the discogs database
func (s *DatabaseService) Artist(artistID int) (*Artist, error) { func (s *DatabaseService) Artist(artistID int) (*Artist, error) {
var artist *Artist var artist *Artist
if err := request(s.url+artistsURI+strconv.Itoa(artistID), nil, &artist); err != nil { err := request(s.url+artistsURI+strconv.Itoa(artistID), nil, &artist)
return nil, err return artist, err
}
return artist, nil
} }
// ArtistReleases ... // ArtistReleases ...
@ -123,10 +115,8 @@ type ArtistReleases struct {
// ArtistReleases returns a list of releases and masters associated with the artist. // ArtistReleases returns a list of releases and masters associated with the artist.
func (s *DatabaseService) ArtistReleases(artistID int, pagination *Pagination) (*ArtistReleases, error) { func (s *DatabaseService) ArtistReleases(artistID int, pagination *Pagination) (*ArtistReleases, error) {
var releases *ArtistReleases var releases *ArtistReleases
if err := request(s.url+artistsURI+strconv.Itoa(artistID)+"/releases", pagination.toParams(), &releases); err != nil { err := request(s.url+artistsURI+strconv.Itoa(artistID)+"/releases", pagination.params(), &releases)
return nil, err return releases, err
}
return releases, nil
} }
// Label resource represents a label, company, recording studio, location, // Label resource represents a label, company, recording studio, location,
@ -148,10 +138,8 @@ type Label struct {
// Label returns a label. // Label returns a label.
func (s *DatabaseService) Label(labelID int) (*Label, error) { func (s *DatabaseService) Label(labelID int) (*Label, error) {
var label *Label var label *Label
if err := request(s.url+labelsURI+strconv.Itoa(labelID), nil, &label); err != nil { err := request(s.url+labelsURI+strconv.Itoa(labelID), nil, &label)
return nil, err return label, err
}
return label, nil
} }
// LabelReleases is a list of Releases associated with the label. // LabelReleases is a list of Releases associated with the label.
@ -163,10 +151,8 @@ type LabelReleases struct {
// LabelReleases returns a list of Releases associated with the label. // LabelReleases returns a list of Releases associated with the label.
func (s *DatabaseService) LabelReleases(labelID int, pagination *Pagination) (*LabelReleases, error) { func (s *DatabaseService) LabelReleases(labelID int, pagination *Pagination) (*LabelReleases, error) {
var releases *LabelReleases var releases *LabelReleases
if err := request(s.url+labelsURI+strconv.Itoa(labelID)+"/releases", pagination.toParams(), &releases); err != nil { err := request(s.url+labelsURI+strconv.Itoa(labelID)+"/releases", pagination.params(), &releases)
return nil, err return releases, err
}
return releases, nil
} }
// Master resource represents a set of similar releases. // Master resource represents a set of similar releases.
@ -192,10 +178,8 @@ type Master struct {
// Master returns a master release // Master returns a master release
func (s *DatabaseService) Master(masterID int) (*Master, error) { func (s *DatabaseService) Master(masterID int) (*Master, error) {
var master *Master var master *Master
if err := request(s.url+mastersURI+strconv.Itoa(masterID), nil, &master); err != nil { err := request(s.url+mastersURI+strconv.Itoa(masterID), nil, &master)
return nil, err return master, err
}
return master, nil
} }
// MasterVersions retrieves a list of all releases that are versions of this master. // MasterVersions retrieves a list of all releases that are versions of this master.
@ -207,8 +191,6 @@ type MasterVersions struct {
// MasterVersions retrieves a list of all Releases that are versions of this master // MasterVersions retrieves a list of all Releases that are versions of this master
func (s *DatabaseService) MasterVersions(masterID int, pagination *Pagination) (*MasterVersions, error) { func (s *DatabaseService) MasterVersions(masterID int, pagination *Pagination) (*MasterVersions, error) {
var versions *MasterVersions var versions *MasterVersions
if err := request(s.url+mastersURI+strconv.Itoa(masterID)+"/versions", pagination.toParams(), &versions); err != nil { err := request(s.url+mastersURI+strconv.Itoa(masterID)+"/versions", pagination.params(), &versions)
return nil, err return versions, err
}
return versions, nil
} }

View File

@ -9,15 +9,16 @@ import (
func ReleaseServer(w http.ResponseWriter, r *http.Request) { func ReleaseServer(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
io.WriteString(w, `{"title":"Elephant Riddim"}`) if _, err := io.WriteString(w, `{"title":"Elephant Riddim"}`); err != nil {
panic(err)
}
} }
func TestReleaseServiceRelease(t *testing.T) { func TestReleaseServiceRelease(t *testing.T) {
expectedTitle := "Elephant Riddim"
ts := httptest.NewServer(http.HandlerFunc(ReleaseServer)) ts := httptest.NewServer(http.HandlerFunc(ReleaseServer))
defer ts.Close() defer ts.Close()
expectedTitle := "Elephant Riddim"
d := initDiscogsClient(t, &Options{URL: ts.URL}) d := initDiscogsClient(t, &Options{URL: ts.URL})
release, err := d.Database.Release(8138518) release, err := d.Database.Release(8138518)
if err != nil { if err != nil {

View File

@ -2,6 +2,7 @@ package discogs
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
@ -85,6 +86,15 @@ func request(path string, params url.Values, resp interface{}) error {
} }
defer response.Body.Close() 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) body, err := ioutil.ReadAll(response.Body)
if err != nil { if err != nil {
return err return err

View File

@ -16,6 +16,7 @@ func (e *Error) Error() string {
// APIErrors // APIErrors
var ( var (
ErrUnauthorized = &Error{"authentication required"}
ErrCurrencyNotSupported = &Error{"currency does not supported"} ErrCurrencyNotSupported = &Error{"currency does not supported"}
ErrUserAgentInvalid = &Error{"invalid user-agent"} ErrUserAgentInvalid = &Error{"invalid user-agent"}
) )

View File

@ -9,7 +9,7 @@ import (
func main() { func main() {
d, err := discogs.NewClient(&discogs.Options{ d, err := discogs.NewClient(&discogs.Options{
UserAgent: "TestDiscogsClient/0.0.1 +http://example.com", UserAgent: "TestDiscogsClient/0.0.1 +http://example.com",
Currency: "AAA", Currency: "USD",
Token: "", Token: "",
}) })
if err != nil { if err != nil {
@ -17,7 +17,7 @@ func main() {
return return
} }
release, err := d.Database.Release(12345) release, err := d.Search.Search(discogs.SearchRequest{Q: "middle", PerPage: 3})
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return

2
go.mod
View File

@ -1,3 +1,3 @@
module github.com/irlndts/go-discogs module github.com/irlndts/go-discogs
require github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 go 1.12

2
go.sum
View File

@ -1,2 +0,0 @@
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=

View File

@ -175,23 +175,15 @@ type Pagination struct {
} }
// toParams converts pagaination params to request values // toParams converts pagaination params to request values
func (p *Pagination) toParams() url.Values { func (p *Pagination) params() url.Values {
if p == nil { if p == nil {
return nil return nil
} }
params := url.Values{} params := url.Values{}
if p.Sort != "" { params.Set("sort", p.Sort)
params.Set("sort", p.Sort) params.Set("sort_order", p.SortOrder)
} params.Set("page", strconv.Itoa(p.Page))
if p.SortOrder != "" { params.Set("per_page", strconv.Itoa(p.PerPage))
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 return params
} }

View File

@ -1,7 +1,8 @@
package discogs package discogs
import ( import (
"github.com/google/go-querystring/query" "net/url"
"strconv"
) )
// SearchService ... // SearchService ...
@ -17,27 +18,56 @@ func newSearchService(url string) *SearchService {
// SearchRequest describes search request // SearchRequest describes search request
type SearchRequest struct { type SearchRequest struct {
Q string `url:"q,omitempty"` // search query Q string // search query
Type string `url:"type,omitempty"` // one of release, master, artist, label Type string // one of release, master, artist, label
Title string `url:"title,omitempty"` // search by combined “Artist Name - Release Title” title field Title string // search by combined “Artist Name - Release Title” title field
ReleaseTitle string `url:"release_title,omitempty"` // search release titles ReleaseTitle string // search release titles
Credit string `url:"credit,omitempty"` // search release credits Credit string // search release credits
Artist string `url:"artist,omitempty"` // search artist names Artist string // search artist names
Anv string `url:"anv,omitempty"` // search artist ANV Anv string // search artist ANV
Label string `url:"label,omitempty"` // search label names Label string // search label names
Genre string `url:"genre,omitempty"` // search genres Genre string // search genres
Style string `url:"style,omitempty"` // search styles Style string // search styles
Country string `url:"country,omitempty"` // search release country Country string // search release country
Year string `url:"year,omitempty"` // search release year Year string // search release year
Format string `url:"format,omitempty"` // search formats Format string // search formats
Catno string `url:"catno,omitempty"` // search catalog number Catno string // search catalog number
Barcode string `url:"barcode,omitempty"` // search barcodes Barcode string // search barcodes
Track string `url:"track,omitempty"` // search track titles Track string // search track titles
Submitter string `url:"submitter,omitempty"` // search submitter username Submitter string // search submitter username
Contributor string `url:"contributor,omitempty"` // search contributor usernames Contributor string // search contributor usernames
Page int `url:"page,omitempty"` Page int
PerPage int `url:"per_page,omitempty"` PerPage int
}
func (r *SearchRequest) params() url.Values {
if r == nil {
return nil
}
params := url.Values{}
params.Set("q", r.Q)
params.Set("type", r.Type)
params.Set("title", r.Title)
params.Set("release_title", r.ReleaseTitle)
params.Set("credit", r.Credit)
params.Set("artist", r.Artist)
params.Set("anv", r.Anv)
params.Set("label", r.Label)
params.Set("genre", r.Genre)
params.Set("style", r.Style)
params.Set("country", r.Country)
params.Set("year", r.Year)
params.Set("format", r.Format)
params.Set("catno", r.Catno)
params.Set("barcode", r.Barcode)
params.Set("track", r.Track)
params.Set("submitter", r.Submitter)
params.Set("contributor", r.Contributor)
params.Set("page", strconv.Itoa(r.Page))
params.Set("per_page", strconv.Itoa(r.PerPage))
return params
} }
// Search describes search response // Search describes search response
@ -68,16 +98,8 @@ type Result struct {
// Issue a search query to our database. This endpoint accepts pagination parameters. // Issue a search query to our database. This endpoint accepts pagination parameters.
// Authentication (as any user) is required. // Authentication (as any user) is required.
// https://www.discogs.com/developers/#page:database,header:database-search // https://www.discogs.com/developers/#page:database,header:database-search
// TODO(irlndts): improve params to pass
func (s *SearchService) Search(req SearchRequest) (*Search, error) { func (s *SearchService) Search(req SearchRequest) (*Search, error) {
params, err := query.Values(req)
if err != nil {
return nil, err
}
var search *Search var search *Search
if err := request(s.url, params, &search); err != nil { err := request(s.url, req.params(), &search)
return nil, err return search, err
}
return search, nil
} }

View File

@ -1,27 +0,0 @@
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.

View File

@ -1,320 +0,0 @@
// 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
}

2
vendor/modules.txt vendored
View File

@ -1,2 +0,0 @@
# github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135
github.com/google/go-querystring/query