Skip to main content

What's the weather like in Honolulu

·481 words·3 mins·
Table of Contents
Building a Wttr client in Go - This article is part of a series.
Part 2: This Article

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.

alrayyes/gwttr

Proof of concept go wttr client

Go
0
0
Building a Wttr client in Go - This article is part of a series.
Part 2: This Article