This afternoon I ran across a rather interesting question on Stack Overflow Gmail API shows empty body when getting message. The author of the question was trying to parse the raw email body from a Gmail get response but the raw field was always returning null.
It didn’t take me long to realize that the issue was that they were using Format(“full”) instead of Format(“raw”) which was most likely the cause of their issues. I have not written many Go tutorials, so I have to admit this took me about an hour to put together. The main reason being that the author of the question did not create a valid How to create a Minimal, Reproducible Example which meant I would need to code everything including the authorization myself.
Google Workspace service account with Gmail
In order to access Gmail api I decided to just use a sample I already had for Service accounts and Google calendar. This would mean that I needed to set up domain wide delegation to a user on my workspace domain.
The first thing I have done is Load the service account key file, once that is loaded I can create my service object. Now remember I am connecting to gmail though a google workspace domain account, this means that I need to delegate to a user on my domain, I did this by setting config.Subject property. Then I can create my service object.
ctx := context.Background() serviceAccountJSON, err := ioutil.ReadFile(ServiceAccount) if err != nil { log.Fatalf("Warning: Unable to load service account key file %v", err) } config, err := google.JWTConfigFromJSON(serviceAccountJSON, SCOPE) if err != nil { log.Fatalf("Warning: Unable to create gmail Client %v", err) } config.Subject = "[Redacted]@daimto.com" srv, err := gmail.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx))) if err != nil { log.Fatalf("Warning: Unable to create drive Client %v", err) }
List Messages
Once I have a service object i can run the user.messages.list method which will list all the messages on my deligated users gmail acocunt.
r, err := srv.Users.Messages.List("me").Do()
if err != nil {
log.Fatalf("Unable to retrieve messages: %v", err)
}
if len(r.Messages) == 0 {
fmt.Println("No messages found.")
return
}
fmt.Println("Labels:")
for _, l := range r.Messages {
fmt.Printf("- %s\n", l.Id)
GetEmail(srv, l.Id)
}
Get Message
Now for the fun part. I have created a function which will get the message and print out the raw data. In order to get the raw data we need to use Format(“RAW”) and then base64 decode it.
func GetEmail(srv *gmail.Service, messageId string) { gmailMessageResposne, err := srv.Users.Messages.Get("me", messageId).Format("RAW").Do() if err != nil { log.Println("error when getting mail content: ", err) } if gmailMessageResposne != nil { decodedData, err := base64.RawURLEncoding.DecodeString(gmailMessageResposne.Raw) if err != nil { log.Println("error b64 decoding: ", err) } fmt.Printf("- %s\n", decodedData) } }
Conclusion
When posting questions on Stack Overflow please give me a full example so that I dont have to go though the process of creating everything manually. ……………. Oh never mind this was fun even if I did have to code the authorization myself.
As you can see its relatively easy to read the raw message data from gmail api using Go. Its just a matter of getting the format right.