Get the weather for Honolulu #
A logical next step would be to get the weather for a single specific location, let’s go with Honolulu for now. To keep
things simple, let’s get the smallest string we can by calling curl "https://wttr.in/honolulu?0"
This should return
something like
Weather report: honolulu
\ / Partly cloudy
_ /"".-. +23(25) °C
\_( ). ↑ 11 km/h
/(___(__) 16 km
0.1 mm
main.go #
The minimum code we can get away with for getting the latest weather in Honolulu would be:
package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
resp, err := http.Get("https://wttr.in/honolulu?0A")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Panic(err)
}
fmt.Println(string(body))
}
Read the response #
Get the response using http.Get(), if that fails return an error with log.Fatal().
This prints the error and calls os.Exit(1), this lets the command return status 1 when something goes wrong. Defer
with resp.Body.Close()
is used to free up the response when the end of the main()
function is reached.
resp, err := http.Get("https://wttr.in/honolulu?0A")
if err != nil {
log.Fatal(err)
}
// close up the response to save resources
defer resp.Body.Close()
Translate the response #
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Panic(err)
}
fmt.Println(string(body))
Response.Body
is an io.ReadCloser. This type is used as the response body could be
multiple things (i.e.: string, JSON etc.).
Therefore, io.ReadAll must first be called to transform the response body into a []bypte
type.
If nothing goes wrong this is converted to a string and outputted to the user.
Note that log.Panic() is called instead of io.LogFatal()
here as io.LogPanic()
calls
the defer
function before exiting.
We can de better #
This solution isn’t great for multiple reasons:
- There is no control of the http timeout
- We can only see the weather in Honolulu
- We’re overly dependent on wttr.in. If the host is down for whatever reason, the tests will fail.
- All the logic is in
main()
, making testing challenging - The wttr domain is hard coded, this prevents us from accessing our own instance
All of these issues and more will be addressed later, for now this is good enough.
main_test.go #
For our test, we’re just going to check that Weather report: honolulu
is included in the command output.
package main
import (
"bytes"
"github.com/stretchr/testify/assert"
"io"
"os"
"testing"
)
func TestCmdOutput(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
main()
_ = w.Close()
os.Stdout = oldStdout
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
got := buf.String()
want := "Weather report: honolulu"
assert.Contains(t, got, want)
}
The only real change in the test is that we check that the response contains the string Weather report: honolulu
.
Source code #
This version of the code can be found here.
Proof of concept go wttr client