Skip to content

Instantly share code, notes, and snippets.

@RicardoLinck
Last active November 1, 2020 11:34
Show Gist options
  • Save RicardoLinck/8be80ba51534861ed079fcd5417f25f4 to your computer and use it in GitHub Desktop.
Save RicardoLinck/8be80ba51534861ed079fcd5417f25f4 to your computer and use it in GitHub Desktop.
package fetch
import (
"encoding/json"
"errors"
"io"
"math"
"net/http"
"sort"
"time"
)
// ErrInvalidAPIVersion represent the error for invalid api version
var ErrInvalidAPIVersion = errors.New("invalid api version")
// Configuration contain parameters to call the api
type Configuration struct {
baseURL string
apiVersion int
fetcher fetcher
}
// NewConfiguration return a new Configuration struct based on provided parameters
func NewConfiguration(baseURL string, apiVersion int) Configuration {
return Configuration{
baseURL: baseURL,
apiVersion: apiVersion,
}
}
// Person unifies responses from both api version
type Person struct {
Name string
Age int
Sports []string
}
type personAPIv1 struct {
Name string `json:"user_name"`
DateOfBirth time.Time `json:"dob"`
Sports []sportAPIv1 `json:"fav_sports"`
}
type sportAPIv1 struct {
Name string `json:"name"`
Order int `json:"order"`
}
type personAPIv2 struct {
Name string `json:"name"`
Age int `json:"age"`
Sports []string `json:"sports"`
}
func getEndpointAndDecodeFunc(version int) (string, func(reader io.Reader) (Person, error), error) {
switch version {
case 1:
return "/api/v1/some-data", decodeAndMapv1, nil
case 2:
return "/api/v2/data", decodeAndMapv2, nil
default:
return "", nil, ErrInvalidAPIVersion
}
}
type fetcher interface {
fetch(url string) (io.ReadCloser, error)
}
type httpFetcher struct{}
func (h httpFetcher) fetch(url string) (io.ReadCloser, error) {
response, err := http.DefaultClient.Get(url)
return response.Body, err
}
// FetchData fetches the data from the api using the configuration
func (c Configuration) FetchData() (Person, error) {
person := Person{}
endpoint, decoderFunc, err := getEndpointAndDecodeFunc(c.apiVersion)
if err != nil {
return person, err
}
response, err := c.fetcher.fetch(c.baseURL + endpoint)
if err != nil {
return person, err
}
defer response.Close()
return decoderFunc(response)
}
func decodeAndMapv1(reader io.Reader) (Person, error) {
personV1 := personAPIv1{}
person := Person{}
err := json.NewDecoder(reader).Decode(&personV1)
if err != nil {
return person, err
}
person.Name = personV1.Name
person.Age = int(math.Floor(time.Since(personV1.DateOfBirth).Hours() / 24 / 365))
sort.Slice(personV1.Sports, func(i, j int) bool {
return personV1.Sports[i].Order < personV1.Sports[j].Order
})
for _, sport := range personV1.Sports {
person.Sports = append(person.Sports, sport.Name)
}
return person, nil
}
func decodeAndMapv2(reader io.Reader) (Person, error) {
personV2 := personAPIv2{}
person := Person{}
err := json.NewDecoder(reader).Decode(&personV2)
if err != nil {
return person, err
}
person.Name = personV2.Name
person.Age = personV2.Age
person.Sports = personV2.Sports
return person, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment