Mailing List
Previously on ATLG, we looked at how we can use Go templates to quickly use marked-up text. This time around we're going to put those new formatting skills to good use! To do that we'll be pulling in the mailgunner
package we put together a few weeks ago. See I told you I had plans for it.
We're going to jump right in and start building on top of what we already have so, you may want to take a moment to read the other two posts if you have not already.
Once again starting at the top! If you've been following the series so far you will no that for our examples we're starting off with the current set of imports. You may notice one new standard library this time around errors
, we'll get to how we are using it in a moment. You may also note, that this time around we've trimmed out most of the unneeded struct parts and just left []Users
with the name
, username
, and email
entries. As I said last time that was really only for demonstration purposes on that post, so I've left it out.
You may also notice that our import from the mailgunner
package, which currently doesn't actually exist up on GitHub, is a little different. Having the leading c
allows us to use the import as just that the letter "c" - so we can type out c.SomeFunc()
instead of using client
. Note that the mailgunner code will be tweaked slightly and put up on GitHub after the next post.
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"text/template"
"github.com/shindakun/envy"
c "github.com/shindakun/mailgunner/client"
)
const apiurl = "https://api.mailgun.net/v3/youremaildomain.com"
type Users []struct {
Name string `json:"name"`
Username string `json:"username"`
Email string `json:"email"`
}
Now we're down to our first real bit of new code. checkStatusCode()
is a small utility (or helper) function which we're using to check to see if the http
response code is 200
or not. The endpoints we're using should only ever respond to our code with a 200 OK
if not we're going to return an error. You might ask why we're returning an error and not just panicking here? That's because we want the code to panic and exit in the code block it's called from, this way you don't have to trace back. Later we'll also look into gracefully handling errors instead of just panicking. Anyway, it is a good idea to always check response codes - we skipped over it in the previous examples but, I'll try to remember doing it from here on out. Maybe we'll start putting together a "utilities" package for stuff that can be reused.
func checkStatusCode(s int) error {
if s != 200 {
err := fmt.Sprintf("unexpected status code: %d", s)
return errors.New(err)
}
return nil
}
sendEmail()
is similar in some respects to the code we wrote to send our test message in the mailgunner
post. This time, however, we're passing in our bytes.Buffer
which will hold our completed email template, and the email address. Look closely and you may notice that I'm swapping the "from" and "to" fields in mgc.FormatEmailRequest()
this is so I can actually send the emails, but send them to a working email - please don't try to email the example ones. They probably are all fake but still spamming isn't polite.
func sendEmail(buf bytes.Buffer, email string) {
mgKey, err := envy.Get("MGKEY")
if err != nil {
panic(err)
}
mgc := c.NewMgClient(apiurl, mgKey)
req, err := mgc.FormatEmailRequest(email, "youremailaddress@",
"Test email", buf.String())
if err != nil {
panic(err)
}
res, err := mgc.Client.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
Here we're using our helper to check the returned status code and panic if we're not looking good. Finally, as with every example so far, we're printing our results to standard out.
if err = checkStatusCode(res.StatusCode); err != nil {
panic(err)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
Alright, now lets dive into our main()
! A lot of this will look familiar if you've read the previous posts. This iteration over the basics is what I'm hoping allows this to stick in my head, and maybe yours too!
func main() {
APIURL := "https://jsonplaceholder.typicode.com/users"
req, err := http.NewRequest(http.MethodGet, APIURL, nil)
if err != nil {
panic(err)
}
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if err = checkStatusCode(resp.StatusCode); err != nil {
panic(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
var u Users
json.Unmarshal(body, &u)
Our message is pretty bland, with a full-featured mailing list app you'd make it easy to load custom messages, etc. But, as always, we're just trying stuff out to learn. If we iterate on this enough it could easily become a full app one day. We'll range
through our users and pass the results of the populated template to sendEmail()
which will do the actual sending.
msgText := "Hi {{.Name}}! Or should I call you, {{.Username}}? There is a new post!\n\n"
t := template.Must(template.New("msg").Parse(msgText))
for _, v := range u {
var buf bytes.Buffer
err := t.Execute(&buf, v)
if err != nil {
panic(err)
}
sendEmail(buf, v.Email)
}
}
And that's it! Next time around we are going to tweak our mailgunner
package a little bit and get it posted up on GitHub so we can actually go get
it!
You can find the code for this and most of the other Attempting to Learn Go posts in the repo on GitHub.
shindakun / atlg
Source repo for the "Attempting to Learn Go" posts I've been putting up over on dev.to
Attempting to Learn Go
Here you can find the code I've been writing for my Attempting to Learn Go posts that I've been writing and posting over on Dev.to.
Post Index
Enjoy this post? |
---|
How about buying me a coffee? |
Top comments (2)
Go is on my list of things to learn, looks like a wonderful language. Thanks for the post.
Thanks for the comment! Go's been great to get into, I've been really enjoying working with it so far!