massive: move a lot of things
This commit is contained in:
38
x-tba/swapi-api-client/film.go
Normal file
38
x-tba/swapi-api-client/film.go
Normal file
@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Film represents a single Star Wars film
|
||||
type Film struct {
|
||||
// All fields should be exported to be decoded
|
||||
Title string `json:"title"`
|
||||
Opening string `json:"opening_crawl"`
|
||||
Starships []string `json:"starships"`
|
||||
}
|
||||
|
||||
func (f Film) String() string {
|
||||
var buf strings.Builder
|
||||
|
||||
fmt.Fprintln(&buf, f.Title)
|
||||
fmt.Fprintln(&buf, strings.Repeat("-", len(f.Title)))
|
||||
buf.WriteByte('\n')
|
||||
fmt.Fprintln(&buf, f.Opening)
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// fetchFilm returns a Film resource by its id
|
||||
func fetchFilm(ctx context.Context, id int) (film Film, err error) {
|
||||
fmt.Println("requesting", fmt.Sprintf(swapi+"films/%d/", id))
|
||||
c, err := request(ctx, fmt.Sprintf(swapi+"films/%d/", id))
|
||||
if err != nil {
|
||||
return film, err
|
||||
}
|
||||
|
||||
return film, json.Unmarshal(c, &film)
|
||||
}
|
84
x-tba/swapi-api-client/main.go
Normal file
84
x-tba/swapi-api-client/main.go
Normal file
@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// const
|
||||
// nil, string, int, float64, bool, comparison
|
||||
// variables
|
||||
// multiple short variables
|
||||
// assignment?
|
||||
// if
|
||||
// error handling
|
||||
// functions
|
||||
// returns
|
||||
// defer
|
||||
// struct
|
||||
// encoding/json
|
||||
// pointers
|
||||
// concurrency
|
||||
// select
|
||||
// chan receive
|
||||
// fmt
|
||||
// Printf
|
||||
// Sprintf
|
||||
// Errorf
|
||||
// net/http
|
||||
// Get
|
||||
// context/Context
|
||||
|
||||
// TODO: convert fmt calls to log
|
||||
// TODO: you can make the fetcher a library and main package the user
|
||||
// TODO: you can generate an html for the ship details? template pkg.
|
||||
|
||||
const timeout = 10 * time.Second
|
||||
|
||||
func main() {
|
||||
args := os.Args[1:]
|
||||
quit("give me a film id", len(args) != 1)
|
||||
|
||||
id, err := strconv.Atoi(args[0])
|
||||
quit("film id is incorrrect", err)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
// TODO: print the ship details to a text file (or any io.Writer)
|
||||
|
||||
film, err := fetchFilm(ctx, id)
|
||||
quit("Error occurred while fetching the film data", err)
|
||||
fmt.Println(film)
|
||||
|
||||
// a channel also can be used to print as they come
|
||||
ships := make([]Starship, len(film.Starships))
|
||||
err = fetchStarships(ctx, film.Starships, ships)
|
||||
quit("Error occurred while fetching starships", err)
|
||||
|
||||
fmt.Println("Ships used in the movie:")
|
||||
fmt.Println("------------------------")
|
||||
for _, ship := range ships {
|
||||
fmt.Println(ship)
|
||||
}
|
||||
}
|
||||
|
||||
func quit(message string, cond interface{}) {
|
||||
var quit bool
|
||||
|
||||
switch v := cond.(type) {
|
||||
case error:
|
||||
quit = true
|
||||
message += ": " + v.Error()
|
||||
case bool:
|
||||
quit = v
|
||||
}
|
||||
|
||||
if quit {
|
||||
fmt.Fprintln(os.Stderr, message)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
44
x-tba/swapi-api-client/request.go
Normal file
44
x-tba/swapi-api-client/request.go
Normal file
@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// MaxResponseSize limits the response bytes from the API
|
||||
const MaxResponseSize = 2 << 16
|
||||
|
||||
// creating a robust http getter (lecture? :))
|
||||
func request(ctx context.Context, url string) ([]byte, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
// Get the error from the context.
|
||||
// It may contain more useful data.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
default:
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("Bad Status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Prevents the api to shoot us unlimited amount of data
|
||||
r := io.LimitReader(resp.Body, MaxResponseSize)
|
||||
|
||||
return ioutil.ReadAll(r)
|
||||
}
|
69
x-tba/swapi-api-client/starship.go
Normal file
69
x-tba/swapi-api-client/starship.go
Normal file
@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Starship represents a Star Wars space ship
|
||||
type Starship struct {
|
||||
// All fields should be exported to be decoded
|
||||
Name string `json:"name"`
|
||||
Credits json.Number `json:"cost_in_credits"`
|
||||
Rating float64 `json:"hyperdrive_rating,string"`
|
||||
}
|
||||
|
||||
func (ship Starship) String() string {
|
||||
var buf strings.Builder
|
||||
|
||||
fmt.Fprintf(&buf, "Ship Name: %s\n", ship.Name)
|
||||
fmt.Fprintf(&buf, "Price : %s\n", ship.Credits)
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// TODO: return error instead of quit()
|
||||
func fetchStarships(ctx context.Context, shipUrls []string, ships []Starship) error {
|
||||
ships = ships[:0]
|
||||
|
||||
re := regexp.MustCompile(`/([0-9]+)/$`)
|
||||
for _, url := range shipUrls {
|
||||
ids := re.FindAllStringSubmatch(url, -1)
|
||||
if ids == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
sid := ids[0][1]
|
||||
id, _ := strconv.Atoi(sid)
|
||||
|
||||
// TODO: goroutine
|
||||
ship, err := fetchStarship(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ships = append(ships, ship)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fetchStarship returns Starship info by its id
|
||||
func fetchStarship(ctx context.Context, id int) (ship Starship, err error) {
|
||||
c, err := request(ctx, fmt.Sprintf(swapi+"starships/%d/", id))
|
||||
if err != nil {
|
||||
return ship, err
|
||||
}
|
||||
|
||||
// -> If your response body is small enough,
|
||||
// just read it all into memory using ioutil.ReadAll
|
||||
// and use json.Unmarshal.
|
||||
//
|
||||
// -> Do not use json.Decoder if you are not dealing
|
||||
// with JSON streaming.
|
||||
|
||||
return ship, json.Unmarshal(c, &ship)
|
||||
}
|
3
x-tba/swapi-api-client/swapi.go
Normal file
3
x-tba/swapi-api-client/swapi.go
Normal file
@ -0,0 +1,3 @@
|
||||
package main
|
||||
|
||||
const swapi = "https://swapi.co/api/"
|
Reference in New Issue
Block a user