1
\$\begingroup\$

The client consumes the Google URL Shortener API. What could I improve in terms of readability and code style? It is my first real project in Go.

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
    "strings"
)

var baseURL *url.URL

func init() {
    var err error
    baseURL, err = url.Parse("https://www.googleapis.com/urlshortener/v1/url")
    if err != nil {
        panic(err)
    }
}

// GoogleShortener communicates with the Google URL shortener API.
type GoogleShortener struct {
    APIKey string
}

func (shortener *GoogleShortener) addAPIKey(query *url.Values) {
    if shortener.APIKey != "" {
        query.Set("key", shortener.APIKey)
    }
}

// GoogleShortenerError represents an error returned by the Google URL shortener API.
type GoogleShortenerError struct {
    Code    int
    Message string
    Errors  map[string]interface{}
}

func (e GoogleShortenerError) Error() string {
    return fmt.Sprintf("Google URL shortener reported failure: %s", e.Message)
}

type shortenResponse struct {
    ShortURL string               `json:"id,ommitempty"`
    Error    GoogleShortenerError `json:"error,ommitempty"`
}

type expandResponse struct {
    LongURL string               `json:"longUrl,ommitempty"`
    Error   GoogleShortenerError `json:"error,ommitempty"`
}

// Shorten requests the short URL of longURL.
func (shortener *GoogleShortener) Shorten(longURL *url.URL) (*url.URL, error) {
    u := baseURL
    query := url.Values{}
    shortener.addAPIKey(&query)
    u.RawQuery = query.Encode()

    payload := url.Values{}
    payload.Set("longUrl", longURL.String())
    resp, err := http.Post(u.String(), "application/x-www-form-urlencoded", strings.NewReader(payload.Encode()))
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    dec := json.NewDecoder(resp.Body)
    var res shortenResponse
    err = dec.Decode(&res)
    if err != nil {
        return nil, err
    }
    if resp.StatusCode != http.StatusOK {
        if res.Error.Code == 0 {
            res.Error.Code = resp.StatusCode
        }
        return nil, res.Error
    }
    shortURL, err := url.Parse(res.ShortURL)
    if err != nil {
        return nil, err
    }
    return shortURL, nil
}

// Expand determines the long URL of shortURL.
func (shortener *GoogleShortener) Expand(shortURL *url.URL) (*url.URL, error) {
    u := baseURL
    query := url.Values{}
    query.Set("shortUrl", shortURL.String())
    shortener.addAPIKey(&query)
    u.RawQuery = query.Encode()

    resp, err := http.Get(u.String())
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    dec := json.NewDecoder(resp.Body)
    var res expandResponse
    err = dec.Decode(&res)
    if err != nil {
        return nil, err
    }
    if resp.StatusCode != http.StatusOK {
        if res.Error.Code == 0 {
            res.Error.Code = resp.StatusCode
        }
        return nil, res.Error
    }
    longURL, err := url.Parse(res.LongURL)
    if err != nil {
        return nil, err
    }
    return longURL, nil
}

// NewGoogleShortener returns a with the given API key configured GoogleShortener.
func NewGoogleShortener(apiKey string) *GoogleShortener {
    return &GoogleShortener{apiKey}
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.