I have an issue with the new NewService functionality of gmail api. If I use the deprecated gmail.New() everything works.
With NewService() I get invalid memory address or nil pointer dereference
My implementation is the following
type MailData struct {
To string
Name string
Subject string
Content template.HTML
}
func doSend(msg *gmail.Message, srv *gmail.Service) error {
_, err := srv.Users.Messages.Send("me", msg).Do()
if err != nil {
return err
}
return nil
}
func ComposeMessage(m models.MailData) *gmail.Message {
var gmailMessage gmail.Message
from := mail.Address{Name: "Sender", Address: os.Getenv("MAIL_FROM")}
replyTo := os.Getenv("MAIL_REPLYTO")
to := mail.Address{Name: m.Name, Address: m.To}
header := make(map[string]string)
header["From"] = from.String()
header["Reply-To"] = replyTo
header["To"] = to.String()
header["Subject"] = m.Subject
header["MIME-Version"] = "1.0"
header["Content-Type"] = "text/html; charset=\"utf-8\""
header["Content-Transfer-Encoding"] = "base64"
var msg string
for k, v := range header {
msg += fmt.Sprintf("%s: %s\r\n", k, v)
}
msg += "\r\n" + string(m.Content)
gmailMessage.Raw = base64.RawURLEncoding.EncodeToString([]byte(msg))
return &gmailMessage
}
Using the old gmail.New() works, but it points out that the function is deprecated, so I need to change it to the new gmail.NewService. Though implementing it like below it doesn't work
func sendGMail(m models.MailData) error {
credentials := "../gmail_credentials.json"
ctx := context.Background()
srv, err := gmail.NewService(
ctx,
option.WithCredentialsFile(credentials),
option.WithScopes("https://www.googleapis.com/auth/gmail.send"),
)
if err != nil {
return errors.New(fmt.Sprintf("unable to retrieve gmail client: %s", err))
}
// Create message
gMessage := ComposeMessage(m)
if err := doSend(gMessage, srv); err != nil {
return errors.New(fmt.Sprintf("could not send mail: %s", err))
}
fmt.Println("Email sent")
return nil
}
Edit: the error I get is
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x9a161b]
goroutine 13 [running]:
golang.org/x/oauth2/authhandler.authHandlerSource.Token({{0xc2fb30, 0xc00003c108}, 0xc0002a1340, 0x0, {0x0, 0x0}})
/home/joss/go/pkg/mod/golang.org/x/[email protected]/authhandler/authhandler.go:48 +0x5b
golang.org/x/oauth2.(*reuseTokenSource).Token(0xc00011d1e0)
/home/joss/go/pkg/mod/golang.org/x/[email protected]/oauth2.go:304 +0xd5
golang.org/x/oauth2.(*Transport).RoundTrip(0xc00011d220, 0xc000124600)
/home/joss/go/pkg/mod/golang.org/x/[email protected]/transport.go:45 +0xa7
net/http.send(0xc000124600, {0xc1d200, 0xc00011d220}, {0xb13600, 0xc000263701, 0x0})
/usr/local/go/src/net/http/client.go:252 +0x5d8
net/http.(*Client).send(0xc000483200, 0xc000124600, {0xc0002637f8, 0x4f49b5, 0x0})
/usr/local/go/src/net/http/client.go:176 +0x9b
net/http.(*Client).do(0xc000483200, 0xc000124600)
/usr/local/go/src/net/http/client.go:725 +0x908
net/http.(*Client).Do(...)
/usr/local/go/src/net/http/client.go:593
google.golang.org/api/internal/gensupport.SendRequest({0x0, 0x0}, 0xb33a63, 0xc000124600)
/home/joss/go/pkg/mod/google.golang.org/[email protected]/internal/gensupport/send.go:43 +0xb8
google.golang.org/api/gmail/v1.(*UsersMessagesSendCall).doRequest(0xc000263e10, {0xb2b9fa, 0x4})
/home/joss/go/pkg/mod/google.golang.org/[email protected]/gmail/v1/gmail-gen.go:6836 +0xa05
google.golang.org/api/gmail/v1.(*UsersMessagesSendCall).Do(0xc000263e10, {0x0, 0x1b, 0xb4e56a})
/home/joss/go/pkg/mod/google.golang.org/[email protected]/gmail/v1/gmail-gen.go:6848 +0x78
github.com/user/mailprj/internal.doSend(0xc0000f6180, 0x12)
/home/joss/user/mailprj/internal/gmail-api.go:159 +0xa5
github.com/user/mailprj/internal.sendGMail({{0xb3cd52, 0x12}, {0xb2b38e, 0x4}, {0xb4e56a, 0x29}, {0xc000610000, 0x8a66}})
/home/joss/user/mailprj/internal/gmail-api.go:149 +0x1b2
github.com/user/mailprj/internal.ListenForGMail.func1()
/home/joss/user/mailprj/internal/gmail-api.go:114 +0xc6
created by github.com/user/mailprj/internal.ListenForGMail
/home/joss/user/mailprj/internal/gmail-api.go:111 +0x25
exit status 2
The issue was not that obvious but easy to solve.
While initializing the gmail.NewService()
I needed to pass the config parameter as option, just like the previous implementation of gmail.New()
was using
So before was
client := getClient(config)
srv, err := gmail.New(client)
And now it is
client := getClient(config)
ctx := context.Background()
srv, err := gmail.NewService(
ctx,
option.WithHTTPClient(client),
)
In more context of the Gmail API implementation for Go, you need to take all the functions used at
https://developers.google.com/gmail/api/quickstart/go or
https://github.com/googleworkspace/go-samples/blob/master/gmail/quickstart/quickstart.go
and put them in your go file. These will generate both an AccessToken and the Credentials which, if you follow the guide correctly, you will need to save as a json file and reuse. credentials.json are used to update your AccessToken.
Now instead of using gmail.New()
, the correct way is to use the function as shown above.
The entire sendGmail function is as follows
func sendGMail(m models.MailData) error {
credentials := "../gmail_credentials.json"
b, err := ioutil.ReadFile(credentials)
if err != nil {
return errors.New(fmt.Sprint("unable to read credentials file:", err))
}
config, err := google.ConfigFromJSON(b, gmail.GmailSendScope)
if err != nil {
return errors.New(fmt.Sprint("unable to parse credentials file config:", err))
}
client := getClient(config)
ctx := context.Background()
srv, err := gmail.NewService(
ctx,
option.WithHTTPClient(client),
option.WithScopes("https://www.googleapis.com/auth/gmail.send"),
)
if err != nil {
return fmt.Errorf("unable to retrieve gmail client: %s", err)
}
// Create message
gMessage := ComposeMessage(m)
if err := doSend(gMessage, srv); err != nil {
return fmt.Errorf("could not send mail: %s", err)
}
fmt.Println("Email sent")
return nil
}