alykoshin/gmail-send

View on GitHub
index.js

Summary

Maintainability
C
1 day
Test Coverage
/**
 * Created by alykoshin on 8/12/14.
 */

'use strict';

const _          = require('lodash');
const path       = require('path');
const nodemailer = require('nodemailer');

/**
 * sendOptions - options for nodemailer
 *
 * @typedef {Object} sendOptions
 * @property {string} options.user
 * @property {string} options.pass
 * @property {(string || string[])} options.files
 * @property {string} options.from
 * @property {string} options.to
 * @property {string} options.replyTo
 * @property {string} options.text
 * @property {string} options.html
 * @property {string} options.attachments - array of `nodemailer`s compatible attachments definitions
 */
/**
 * @callback sendCallback
 * @param {Object} error
 * @param {string} result
 * @param {Object} fullResult
 */
/**
 * @constructor
 * @param {sendOptions} options  - options for underlying nodemailer
 * @type {Function}
 */
const GMailSend = function (options) {
  const self = this;

  /** @member {string} */
  self.options = options;

  /** helper to build 'Some Name <some.name@domain.com>' **/
  function prepareAddress(name, address) {
    return name + ' ' + '<' + address + '>';
  }


  function _send(options, callback) {

    const handleSuccess = (info) => {
      if (callback) {
        callback(null, info.response, info);
        //} else { //
        //  const result = {
        //    result: info && info.response,
        //    full:   info,
        //  };
        //  resolve(result);
      }
    };

    const handleError = (error) => {
      if (typeof error === 'string') error = new Error(error);
      if (callback) {
        callback(error, error.message, undefined);
        //} else {
        //  reject(error);
      }
    };

    options.from = options.from || options.user;
    //options.replyTo = options.replyTo || options.user;

    // Configure email transport

    const TRANSPORT = {
      service: 'Gmail', auth: { user: options.user, pass: options.pass }
    };

    const smtpTransport = nodemailer.createTransport(TRANSPORT);

    // Preparing nodemailer options (and attachments)

    // File attachments

    options.files = options.files || [];
    if (!Array.isArray(options.files)) options.files = [ options.files ];
    //if (typeof options.files === 'string') { options.files = [options.files]; }

    options.attachments = options.attachments || [];
    for (let i = 0; i < options.files.length; i++) {
      let file = options.files[ i ];
      // if string is passed, convert it to `nodemailer` attachment object
      if (typeof file === 'string') {
        file = {
          path: file,
          //filename: path.basename( file ),
          //cid:      path.basename( file ),
        };
      }
      if (!file.path) {
        //const msg = 'file/filepath to attach must be set';
        //if (callback) callback(new Error(msg), msg, undefined);
        //return reject()
        return handleError('file/filepath to attach must be set');
      }
      if (typeof file.filename === 'undefined') file.filename = path.basename(file.path);
      if (typeof file.cid === 'undefined') file.cid = file.filename;
      // we do not validate if options.files[i] is really object and has valid properties
      // add to options.attachments used by `nodemailer`
      options.attachments.push(file);
    }
    delete options.files; // remove files property as incompatible with options of underlying `nodemailer`

    // from

    options.from = prepareAddress(options.from, options.user); // adjust to nodemailer format

    // to

    if (typeof options.to === 'string') {
      options.to = prepareAddress(options.to, options.to);   // adjust to nodemailer format

    } else if (Array.isArray(options.to)) {
      let to     = options.to.map((addr) => prepareAddress(addr, addr));
      options.to = to.join(',');
    }

    // Sending email

    //console.log('gmail-send: send(): mailOptions: ', options);
    return smtpTransport.sendMail(options, function (error, info) {
      if (error) {
        //console.log('gmail-send: send(): Error sending message:', error);
        //if (callback) callback(error);
        //return reject(error);
        return handleError(error);

      } else {
        //console.log("gmail-send: send(): Message sent: " + info.response);
        //if (callback) callback(null, info.response, info);
        //return resolve(info.response, info);
        return handleSuccess(info);
      }
    }); // smtpTransport.sendMail()

  } // function _send()


  /**
   * Send email
   *
   * You may use almost any option available in Nodemailer,
   * but if you need fine tuning I'd recommend to consider using Nodemailer directly.
   *
   * @param {sendOptions}  [options]  - options for underlying nodemailer
   * @param {sendCallback} [callback]
   * @return Promise({{ result: string, full: object }})
   */
  self.send = function (options, callback) {
    if (arguments.length === 1)
      if (typeof options === 'function') { // only callback function is provided
        callback = options;
        options  = {};
      }

    options = options || {};
    options = _.extend({}, self.options, options);
    if (!options.user || !options.pass) {
      throw new Error('options.user and options.pass are mandatory.');
    }


    if (callback) return _send(
      options,
      callback,
    );
    else return new Promise((resolve, reject) =>
      _send(
        options,
        (error, result, full) => error
                                 ? reject(error)
                                 : resolve({ result, full })
      ) // _send()
    ); // return new Promise()

  }; // self.send = function()

  return self;
};

//

/**
 * Exporting function to send email
 *
 * @param {sendOptions} options  - options for new GMailSend()
 * @returns {function}
 */
module.exports = function (options) {
  return new GMailSend(options).send;
};