sendgrid / sendgrid-nodejs

The Official Twilio SendGrid Led, Community Driven Node.js API Library
https://sendgrid.com
MIT License
3k stars 781 forks source link

Can't send HTML #369

Closed teddis closed 7 years ago

teddis commented 7 years ago

Issue Summary

I'm using Sendgrid's NodeJS package to send text and/or HTML email. The package (1) never returns from sg.API promise when sending text/html, (2) doesn't update the Dashboard's activity as being sent, and (3) doesn't send the message. However, sending text/plain works fine. Using Curl sends HTML fine as well.

Steps to Reproduce

See attached files. test_email.txt email.txt

test_email.txt

import "source-map-support/register";
import {sendEmail} from "../src/util/email";
import * as fs from "fs";
import {mjml2html} from "mjml";
import chai = require("chai");
import chaiAsPromised = require("chai-as-promised");
import mocha = require("mocha");

const should = chai.should();

chai.use(chaiAsPromised);

describe("Send Email", function () {
    it('should send plain text email', function () {
        const text = "This is a test_email!\n\n" + new Date().toString();
        return sendEmail({
            to: "recipient@email.com",
            subject: "Text test " + new Date().toString(),
            text: text
        }).should.eventually.be.fulfilled;
    });
    it.only('should send simple html email', function () {
        sendEmail({
            to: "recipient@email.com",
            subject: "Simple HTML test " + new Date().toString(),
            html: ' <html><body>some text here</body></html>'
        }).should.eventually.be.fulfilled;
    });
    it('should send html email', function () {
        const mjml = fs.readFileSync(process.cwd() + '/data/mjml/racoon.mjml').toString('utf-8');
        const html = mjml2html(mjml).html;
        fs.writeFileSync('/tmp/racoon.html', html);
        sendEmail({
            to: "recipient@email.com",
            subject: "HTML test " + new Date().toString(),
            html: html
        }).should.eventually.be.fulfilled;
    });
    it('should send html and text email', function () {
        const text = "This is a test_email!\n\n" + new Date().toString();
        const mjml = fs.readFileSync(process.cwd() + '/data/mjml/racoon.mjml').toString('utf-8');
        const html = mjml2html(mjml).html;
        sendEmail({
            to: “recipient@email.com“,
            subject: "HTML + Text Test " + new Date().toString(),
            html: html,
            text: text
        }).should.eventually.be.fulfilled;
    });
});

email.txt

import * as Promise from "bluebird";
const sg = require('sendgrid')(process.env.SENDGRID_KEY);

export function sendEmail({to, subject, html, text}: {to: string, subject: string, html?: string, text?: string}): Promise<any> {
    const helper = require('sendgrid').mail;
    const mail = new helper.Mail();
    const personalization = new helper.Personalization();

    mail.setSubject(subject);
    mail.setFrom(new helper.Email(process.env.FROM_EMAIL, "Support"));
    personalization.addTo(new helper.Email(to));
    mail.addPersonalization(personalization);

    if (text) {
        mail.addContent(new helper.Content('text/plain', text));
    }
    if (html) {
        mail.addContent(new helper.Content('text/html', html));
    }

    const request = sg.emptyRequest({
        method: 'POST',
        path: '/v3/mail/send',
        body: mail.toJSON(),
    });

    return sg.API(request).then(response => {
        console.log(response);
    }).catch(err =>
        console.error(err)
    );
}

Technical details:

thinkingserious commented 7 years ago

Hello @teddis,

At first glance, I don't see any obvious error. I've tested the code in sendEmail and it appears to work as designed. I will add this to our backlog for further investigation.

Meanwhile, I have a few questions:

  1. Are any errors returned (via console.error(err))? Is anything logged on your end (via console.log(response))?
  2. For each call, it may be helpful to log the result of mail.toJSON() to make sure the payload is correct.

With Best Regards,

Elmer

teddis commented 7 years ago

Thanks, Elmer. The sg.API promise doesn't seem to return at all which is odd, so nothing is logged for success or failure. The JSON output logged for mail.toJSON is the following but looks fine to me:

{ from: { email: 'support@orderscape.com', name: 'Support' },
  personalizations: 
   [ Personalization {
       tos: [ { email: 'ted@orderscape.com', name: undefined } ],
       ccs: undefined,
       bccs: undefined,
       subject: undefined,
       headers: undefined,
       substitutions: undefined,
       custom_args: undefined,
       send_at: undefined,
       addTo: [Function],
       getTos: [Function],
       addCc: [Function],
       getCcs: [Function],
       addBcc: [Function],
       getBccs: [Function],
       setSubject: [Function],
       getSubject: [Function],
       addHeader: [Function],
       getHeaders: [Function],
       addSubstitution: [Function],
       getSubstitutions: [Function],
       addCustomArg: [Function],
       getCustomArgs: [Function],
       setSendAt: [Function],
       getSendAt: [Function],
       toJSON: [Function] } ],
  subject: 'Simple HTML test Wed Mar 15 2017 13:47:25 GMT-0400 (AST)',
  content: 
   [ { type: 'text/html',
       value: '<html><body><h1>some text here</h1></body></html>' } ],
  attachments: undefined,
  template_id: undefined,
  sections: undefined,
  headers: undefined,
  categories: undefined,
  custom_args: undefined,
  send_at: undefined,
  batch_id: undefined,
  asm: undefined,
  ip_pool_name: undefined,
  mail_settings: undefined,
  tracking_settings: undefined,
  reply_to: undefined }
teddis commented 7 years ago

Btw I also tried using the callback version which also did not call back:

    return new Promise((resolve, reject) => {
        sg.API(request, (response) => {
            console.log("*** response:", response);
            resolve();
        });
    });

Something must be wrong with the request?

thinkingserious commented 7 years ago

Thanks for digging deeper @teddis,

Can you compare the mail.toJSON output with the successful plain/text call?

teddis commented 7 years ago

Only subject and contents are slightly different. Again, there is no output from the successful plain/test call, but the side effect is that I receive the email, unlike HTML.

thinkingserious commented 7 years ago

Can you try hard coding the html in your email.txt file.

Also, can you try without using the Mail Helper? https://github.com/sendgrid/sendgrid-nodejs#without-mail-helper-class

teddis commented 7 years ago

@thinkingserious, ok getting somewhere. I tried hard coding without mail helper and no difference btw. The trick to get error below was not using mocha which was hiding it for some reason. Why is my auth grant a problem? I created a new API key the other day. I've gone through several to try and fix this before.

sendgrid request: { host: '',
  method: 'POST',
  path: '/v3/mail/send',
  headers: {},
  body: 
   { personalizations: 
      [ { to: [ { email: 'ted@orderscape.com' } ],
          subject: 'Simple HTML test Wed Mar 15 2017 15:41:10 GMT-0400 (AST)' } ],
     from: { email: 'support@orderscape.com', name: 'Support' },
     content: 
      [ { type: 'text/html',
          value: '<html><body><h1>some text here</h1></body></html>' } ] },
  queryParams: {},
  test: false,
  port: '' }

*** error { SendGridError: Response error
    at /Users/ted/src/orderscape/foodiebot/node_modules/sendgrid/lib/sendgrid.js:100:23
    at IncomingMessage.<anonymous> (/Users/ted/src/orderscape/foodiebot/node_modules/sendgrid-rest/lib/client.js:108:9)
    at emitNone (events.js:91:20)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:974:12)
    at _combinedTickCallback (internal/process/next_tick.js:74:11)
    at process._tickCallback (internal/process/next_tick.js:98:9)
  message: 'Response error',
  response: 
   { statusCode: 401,
     body: 
      { errors: 
         [ { message: 'The provided authorization grant is invalid, expired, or revoked',
             field: null,
             help: null } ] },
     headers: 
      { server: 'nginx',
        date: 'Wed, 15 Mar 2017 19:41:10 GMT',
        'content-type': 'application/json',
        'content-length': '116',
        connection: 'close',
        'x-frame-options': 'DENY',
        'access-control-allow-origin': 'https://sendgrid.api-docs.io',
        'access-control-allow-methods': 'POST',
        'access-control-allow-headers': 'Authorization, Content-Type, On-behalf-of, x-sg-elas-acl',
        'access-control-max-age': '600',
        'x-no-cors-reason': 'https://sendgrid.com/docs/Classroom/Basics/API/cors.html' } } }
thinkingserious commented 7 years ago

Hi @teddis,

With regards to your API Key, this is an issue our support team can help with. They can be reached at https://support.sendgrid.com

Thanks for sharing that mocha tidbit, I'm sure that will help someone with a similar issue in the future.

With Best Regards,

Elmer