Skip to content

Commit

Permalink
switching to Google's querystring package
Browse files Browse the repository at this point in the history
this cleans up the struct field tags for every struct
that gets encoded into a query string (json -> url). It also
allows for cleanly using the basic types (e.g. Beer, Event)
as encodable "request" types, e.g. `UpdateBeer(beerID string, b *Beer)`.

Closes #9.
  • Loading branch information
naegelejd committed May 14, 2015
1 parent 4f52cb1 commit 0342edc
Show file tree
Hide file tree
Showing 31 changed files with 344 additions and 465 deletions.
2 changes: 1 addition & 1 deletion adjunct.go
Expand Up @@ -32,7 +32,7 @@ func (as *AdjunctService) List(page int) (al AdjunctList, err error) {
// GET: /adjuncts

var req *http.Request
req, err = as.c.NewRequest("GET", "/adjuncts", Page{page})
req, err = as.c.NewRequest("GET", "/adjuncts", &Page{page})
if err != nil {
return
}
Expand Down
2 changes: 2 additions & 0 deletions adjunct_test.go
Expand Up @@ -17,8 +17,10 @@ func TestAdjunctList(t *testing.T) {
}
defer data.Close()

const page = 1
mux.HandleFunc("/adjuncts/", func(w http.ResponseWriter, r *http.Request) {
checkMethod(t, r, "GET")
checkPage(t, r, page)
io.Copy(w, data)
})

Expand Down
129 changes: 55 additions & 74 deletions beer.go
Expand Up @@ -81,26 +81,26 @@ const (
// BeerListRequest contains all the required and optional fields
// used for querying for a list of Beers.
type BeerListRequest struct {
Page int `json:"p"`
IDs string `json:"ids,omitempty"` // IDs of the beers to return, comma separated. Max 10.
Name string `json:"name,omitempty"`
ABV string `json:"abv,omitempty"`
IBU string `json:"ibu,omitempty"`
GlasswareID int `json:"glasswareId,omitempty"`
SrmID int `json:"srmId,omitempty"`
AvailableID int `json:"availableId,omitempty"`
StyleID int `json:"styleId,omitempty"`
IsOrganic string `json:"isOrganic,omitempty"` // Y/N
HasLabels string `json:"hasLabels,omitempty"` // Y/N
Year int `json:"year,omitempty"` // YYYY
Since string `json:"since,omitempty"` // UNIX timestamp format. Max 30 days
Status string `json:"status,omitempty"`
Order BeerOrder `json:"order,omitempty"`
Sort ListSort `json:"sort,omitempty"`
RandomCount string `json:"randomCount,omitempty"` // how many random beers to return. Max 10
WithBreweries string `json:"withBreweries,omitempty"` // Y/N
WithSocialAccounts string `json:"withSocialAccounts,omitempty"` // Premium. Y/N
WithIngredients string `json:"withIngredients,omitempty"` // Premium. Y/N
Page int `url:"p"`
IDs string `url:"ids,omitempty"` // IDs of the beers to return, comma separated. Max 10.
Name string `url:"name,omitempty"`
ABV string `url:"abv,omitempty"`
IBU string `url:"ibu,omitempty"`
GlasswareID int `url:"glasswareId,omitempty"`
SrmID int `url:"srmId,omitempty"`
AvailableID int `url:"availableId,omitempty"`
StyleID int `url:"styleId,omitempty"`
IsOrganic string `url:"isOrganic,omitempty"` // Y/N
HasLabels string `url:"hasLabels,omitempty"` // Y/N
Year int `url:"year,omitempty"` // YYYY
Since string `url:"since,omitempty"` // UNIX timestamp format. Max 30 days
Status string `url:"status,omitempty"`
Order BeerOrder `url:"order,omitempty"`
Sort ListSort `url:"sort,omitempty"`
RandomCount string `url:"randomCount,omitempty"` // how many random beers to return. Max 10
WithBreweries string `url:"withBreweries,omitempty"` // Y/N
WithSocialAccounts string `url:"withSocialAccounts,omitempty"` // Premium. Y/N
WithIngredients string `url:"withIngredients,omitempty"` // Premium. Y/N
}

// Availability contains information on a Beer's availability.
Expand All @@ -119,42 +119,45 @@ type SRM struct {

// Beer contains all relevant information for a single Beer.
type Beer struct {
ID string
Name string
Description string
FoodPairings string
OriginalGravity string
ABV string
IBU string
GlasswareID int
Glass Glass
StyleID int
Style Style
IsOrganic string
ID string `url:"-"`
Name string `url:"name"` // Required
Description string `url:"description,omitempty"`
FoodPairings string `url:"foodPairings,omitempty"`
OriginalGravity string `url:"originalGravity,omitempty"`
ABV string `url:"abv,omitempty"`
IBU string `url:"ibu,omitempty"`
GlasswareID int `url:"glasswareId,omitempty"`
Glass Glass `url:"-"`
StyleID int `url:"styleId"` // Required
Style Style `url:"-"`
IsOrganic string `url:"isOrganic,omitempty"`
Labels struct {
Medium string
Large string
Icon string
}
ServingTemperature BeerTemperature
ServingTemperatureDisplay string
Status string
StatusDisplay string
AvailableID int
Available Availability
BeerVariationID string
Medium string `url:"-"`
Large string `url:"-"`
Icon string `url:"-"`
} `url:"-"`
Label string `url:"label,omitempty"` // base64. Only used for adding/updating Beers.
Brewery []string `url:"brewery,omitempty"` // breweryID list. Only used for adding/updating Beers.
ServingTemperature BeerTemperature `url:"servingTemperature,omitempty"`
ServingTemperatureDisplay string `url:"-"`
Status string `url:"-"`
StatusDisplay string `url:"-"`
AvailableID int `url:"availableId,omitempty"`
Available Availability `url:"-"`
BeerVariationID string `url:"beerVariationId,omitempty"`
BeerVariation struct {
// TODO: instance of a Beer??
}
SrmID int
SRM SRM
Year int
} `url:"-"`
SrmID int `url:"srmID,omitempty"`
SRM SRM `url:"-"`
Year int `url:"year,omitempty"`
CreateDate string `url:"-"`
UpdateDate string `url:"-"`
}

// List returns all Beers on the page specified in the given BeerListRequest.
func (bs *BeerService) List(q *BeerListRequest) (bl BeerList, err error) {
// GET: /beers

var req *http.Request
req, err = bs.c.NewRequest("GET", "/beers", q)
if err != nil {
Expand Down Expand Up @@ -187,33 +190,11 @@ func (bs *BeerService) Get(id string) (beer Beer, err error) {
return beerResp.Data, nil
}

// BeerChangeRequest contains all the relevant options available to change
// an existing beer record in the BreweryDB.
// TODO: remove this and just use type Beer
type BeerChangeRequest struct {
Name string `json:"name"` // Required
StyleID int `json:"styleId"` // Required
Description string `json:"description"`
ABV string `json:"abv"`
IBU string `json:"ibu"`
GlasswareID int `json:"glasswareId"`
SrmID int `json:"srmID"`
AvailableID int `json:"availableId"`
IsOrganic string `json:"isOrganic"`
BeerVariationID string `json:"beerVariationId"`
Year int `json:"year"`
FoodPairings string `json:"foodPairings"`
ServingTemperature BeerTemperature `json:"servingTemperature"`
OriginalGravity string `json:"originalGravity"`
Brewery string `json:"brewery"` // Comma separated list of existing brewery IDs
Label string `json:"label"` // Base 64 encoded image
}

// Add adds a new Beer to the BreweryDB and returns its new ID on success.
func (bs *BeerService) Add(q *BeerChangeRequest) (id string, err error) {
func (bs *BeerService) Add(b *Beer) (id string, err error) {
// POST: /beers
var req *http.Request
req, err = bs.c.NewRequest("POST", "/beers", q)
req, err = bs.c.NewRequest("POST", "/beers", b)
if err != nil {
return
}
Expand All @@ -231,9 +212,9 @@ func (bs *BeerService) Add(q *BeerChangeRequest) (id string, err error) {
}

// Update changes an existing Beer in the BreweryDB.
func (bs *BeerService) Update(id string, q *BeerChangeRequest) error {
func (bs *BeerService) Update(id string, b *Beer) error {
// PUT: /beer/:beerId
req, err := bs.c.NewRequest("PUT", "/beer/"+id, q)
req, err := bs.c.NewRequest("PUT", "/beer/"+id, b)
if err != nil {
return err
}
Expand Down
8 changes: 7 additions & 1 deletion beer_test.go
Expand Up @@ -19,12 +19,18 @@ func TestBeerList(t *testing.T) {
}
defer data.Close()

const abv = "8"
mux.HandleFunc("/beers/", func(w http.ResponseWriter, r *http.Request) {
checkMethod(t, r, "GET")
abv := r.FormValue("abv")
if v := r.FormValue("abv"); v != abv {
t.Fatalf("Request.FormValue abv = %v, wanted %v", v, abv)
}
// TODO: check more request query values
io.Copy(w, data)
})

bl, err := client.Beer.List(&BeerListRequest{ABV: "8"})
bl, err := client.Beer.List(&BeerListRequest{ABV: abv})
if err != nil {
t.Fatal(err)
}
Expand Down
53 changes: 27 additions & 26 deletions brewery.go
Expand Up @@ -34,38 +34,39 @@ type BreweryList struct {

// Brewery contains all relevant information for a single Brewery.
type Brewery struct {
ID string
Description string
Name string
CreateDate string
MailingListURL string
UpdateDate string
ID string `url:"-"`
Name string `url:"name"`
Description string `url:"description,omitempty"`
MailingListURL string `url:"mailingListUrl,omitempty"`
Images struct {
Medium string
Small string
Icon string
}
Established string
IsOrganic string
Website string
Status string
StatusDisplay string
Medium string `url:"-"`
Small string `url:"-"`
Icon string `url:"-"`
} `url:"-"`
Image string `url:"image,omitempty"` // only used for adding/update Breweries
Established string `url:"established,omitempty"`
IsOrganic string `url:"isOrganic,omitempty"`
Website string `url:"website,omitempty"`
Status string `url:"-"`
StatusDisplay string `url:"-"`
CreateDate string `url:"-"`
UpdateDate string `url:"-"`
}

// BreweryListRequest contains all the required and optional fields
// used for querying for a list of Breweries.
type BreweryListRequest struct {
Page int `json:"p"`
Name string `json:"name,omitempty"`
IDs string `json:"ids,omitempty"`
Established string `json:"established,omitempty"`
IsOrganic string `json:"isOrganic,omitempty"`
HasImages string `json:"hasImages,omitempty"`
Since string `json:"since,omitempty"`
Status string `json:"status,omitempty"`
Order BreweryOrder `json:"order,omitempty"` // TODO: enumerate
Sort string `json:"sort,omitempty"` // TODO: enumerate
RandomCount string `json:"randomCount,omitempty"`
Page int `url:"p"`
Name string `url:"name,omitempty"`
IDs string `url:"ids,omitempty"`
Established string `url:"established,omitempty"`
IsOrganic string `url:"isOrganic,omitempty"`
HasImages string `url:"hasImages,omitempty"`
Since string `url:"since,omitempty"`
Status string `url:"status,omitempty"`
Order BreweryOrder `url:"order,omitempty"` // TODO: enumerate
Sort string `url:"sort,omitempty"` // TODO: enumerate
RandomCount string `url:"randomCount,omitempty"`
// TODO: premium account parameters
}

Expand Down
7 changes: 6 additions & 1 deletion brewery_test.go
Expand Up @@ -19,12 +19,17 @@ func TestBreweryList(t *testing.T) {
}
defer data.Close()

const established = "1988"
mux.HandleFunc("/breweries/", func(w http.ResponseWriter, r *http.Request) {
checkMethod(t, r, "GET")
if v := r.FormValue("established"); v != established {
t.Fatalf("Request.FormValue established = %v, wanted %v", v, established)
}
// TODO: check more request query values
io.Copy(w, data)
})

bl, err := client.Brewery.List(&BreweryListRequest{Established: "1988"})
bl, err := client.Brewery.List(&BreweryListRequest{Established: established})
if err != nil {
t.Fatal(err)
}
Expand Down
8 changes: 6 additions & 2 deletions brewerydb.go
Expand Up @@ -10,6 +10,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/google/go-querystring/query"
"io"
"net/http"
)
Expand All @@ -19,7 +20,7 @@ var apiURL = "http://api.brewerydb.com/v2"
// Page is a convenience type for encoding only a page number
// when paginating lists.
type Page struct {
P int `json:"p"`
P int `url:"p"`
}

// Client serves as the interface to the BreweryDB API.
Expand Down Expand Up @@ -85,7 +86,10 @@ func (c *Client) NewRequest(method string, endpoint string, data interface{}) (*
url := apiURL + endpoint + "/?key=" + c.apiKey
var body io.Reader
if data != nil {
vals := encode(data)
vals, err := query.Values(data)
if err != nil {
return nil, err
}
payload := vals.Encode()
if method == "GET" {
url += "&" + payload
Expand Down
9 changes: 9 additions & 0 deletions brewerydb_test.go
Expand Up @@ -3,6 +3,7 @@ package brewerydb
import (
"net/http"
"net/http/httptest"
"strconv"
"testing"
)

Expand All @@ -25,8 +26,16 @@ func teardown() {
server.Close()
}

// Checks that the HTTP Request's method matches the given method.
func checkMethod(t *testing.T, r *http.Request, method string) {
if method != r.Method {
t.Errorf("Request method = %v, want %v", r.Method, method)
}
}

// Checks that the HTTP Request contains a key "p" with a value matching the given page.
func checkPage(t *testing.T, r *http.Request, page int) {
if p := r.FormValue("p"); p != strconv.Itoa(page) {
t.Fatalf("Request.FormValue p = %v, want %v", p, page)
}
}

0 comments on commit 0342edc

Please sign in to comment.