diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a9f711 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/challenge diff --git a/README.md b/README.md index 43190c7..e85ad99 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +# Usage + +```bash +LOG_LEVEL=debug go run . +``` + +# Original Readme + ## Challenge1 - Work with APIs and Libraries ### Objectives diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..74d0a22 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module challenge + +go 1.19 + +require ( + github.com/sirupsen/logrus v1.9.0 // indirect + golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..64cb7ad --- /dev/null +++ b/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..e35096b --- /dev/null +++ b/main.go @@ -0,0 +1,23 @@ +package main + +import ( + log "github.com/sirupsen/logrus" + "os" +) + +func init() { + log.SetFormatter(&log.JSONFormatter{}) + log.SetOutput(os.Stdout) + desiredLogLevel := os.Getenv("LOG_LEVEL") + level, err := log.ParseLevel(desiredLogLevel) + if err != nil { + log.Errorf("Failed to parse log level: %s", desiredLogLevel) + log.SetLevel(log.InfoLevel) + } else { + log.SetLevel(level) + } +} + +func main() { + _, _ = AllStarships() +} \ No newline at end of file diff --git a/swapi.go b/swapi.go new file mode 100644 index 0000000..bfd86fd --- /dev/null +++ b/swapi.go @@ -0,0 +1,96 @@ +package main + +import ( + "encoding/json" + log "github.com/sirupsen/logrus" + "net/http" +) + +const API_PREFIX = "https://swapi.dev/api" + +// Source: https://swapi.dev/documentation#people +type Person struct { + Name string + // ... other fields unused by this application +} + +// Source: https://swapi.dev/documentation#starships +type Starship struct { + Name string + Pilots []string // URLs to retrieve [People] + // ... other fields unused by this application +} + +// Structs which may be retrieved in a paginated fasion from swapi.dev +type Pageable interface { + Starship | Person +} + +type Gettable interface { + Starship | Person +} + +// Represents a single page of [Pageable] structs +type PageOf[T Pageable] struct { + Count uint + Next *string + Previous *string + Results []T +} + +// Used for retrieving data from swapi.dev +// +// resp, err := Get("/starships") +// +// If [path] does not start with "/", it assumes you have provided a full URL to +// make following [PageOf]'s [Next] and [Previous] simpler. +// +// resp, err := Get("https://git.lyte.dev/swagger.v1.json") +func Get[T interface{}](path string, data *T) error { + var url string + if path[0] == '/' { + url = API_PREFIX + path + } else { + url = path + } + resp, err := http.Get(url) + if err != nil { + log.Errorf("Error GET'ing %s: %+v", url, err) + } + defer resp.Body.Close() + err = json.NewDecoder(resp.Body).Decode(data) + if err != nil { + log.Errorf("Failed to decode response JSON: %+v", err) + } + log.Debugf("GET %s: %+v", url, resp) + return err +} + +func AllStarships() (*[]Starship, error) { + results := make([]Starship, 0) + var page PageOf[Starship] + var resp *http.Response + + err := Get("/starships", &page) + if err != nil { + return &[]Starship{}, err + } + err = json.NewDecoder(resp.Body).Decode(&page) + log.Debugf("Page: %+v", page) + if err != nil { + log.Errorf("Error decoding response body: %+v", err) + } + results = append(results, page.Results...) + + // TODO: loop + for page.Next != nil { + err := Get(*page.Next, &page) + log.Debugf("Page: %+v", page) + if err != nil { + return &[]Starship{}, err + } + json.NewDecoder(resp.Body).Decode(&page) + results = append(results, page.Results...) + } + return &results, nil +}