<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: David James</title>
    <description>The latest articles on Forem by David James (@daviddeejjames).</description>
    <link>https://forem.com/daviddeejjames</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3154%2F96996a92-a3f4-44d0-a6ca-9988676bdf46.png</url>
      <title>Forem: David James</title>
      <link>https://forem.com/daviddeejjames</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/daviddeejjames"/>
    <language>en</language>
    <item>
      <title>Email automation with Node and Dropbox</title>
      <dc:creator>David James</dc:creator>
      <pubDate>Sun, 11 Feb 2018 17:51:09 +0000</pubDate>
      <link>https://forem.com/daviddeejjames/email-automation-with-node-and-dropbox-593o</link>
      <guid>https://forem.com/daviddeejjames/email-automation-with-node-and-dropbox-593o</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it. - Bill Gates&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This quote, I feel, describes why I love being a programmer. We are generally lazy yet quite good at solving problems, including our own! Sometimes, this may not involve even writing code, but most of the time you can assume we did 🤓&lt;/p&gt;

&lt;p&gt;So, my most recent problem: My Mum is required to email an invoice to her client fortnightly, and is fairly computer illiterate. This is handwritten and needs to be scanned as a PDF, and because I’m not around the house much anymore, my sister scans it for her, but she is too lazy to organise the email, so she adds it to my Dropbox so that, finally, I can email it to the client.&lt;/p&gt;

&lt;p&gt;I hate this entire process... and receiving "Have you sent that invoice?" text messages.&lt;/p&gt;

&lt;h4&gt;
  
  
  The steps involved for me are:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Downloading the file from Dropbox&lt;/li&gt;
&lt;li&gt;Logging into Mum's email account&lt;/li&gt;
&lt;li&gt;Typing a very generic email to the client&lt;/li&gt;
&lt;li&gt;Attaching downloaded file&lt;/li&gt;
&lt;li&gt;Send the email&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: AUTOMATE ALL THE THINGS!&lt;/p&gt;

&lt;h2&gt;
  
  
  Javacript/Node to the rescue!
&lt;/h2&gt;

&lt;p&gt;Javascript and Node seemed the most appropriate for my solution as I knew I would need to run a server-side application to check my Dropbox regularly to find the file. I am also in the process of trying to become more of a fullstack developer, so I knew this would be a great learning exercise.&lt;/p&gt;

&lt;p&gt;Recently I had completed &lt;a href="https://learnnode.com/" rel="noopener noreferrer"&gt;Wes Bos' Learn Node&lt;/a&gt; course which greatly assisted in the design choices for my final solution. This included but was not limited to: Node, ES6, &lt;a href="https://scotch.io/tutorials/javascript-promises-for-dummies" rel="noopener noreferrer"&gt;Promises&lt;/a&gt;, &lt;a href="https://github.com/nodemailer/nodemailer" rel="noopener noreferrer"&gt;Nodemailer&lt;/a&gt;, &lt;a href="https://github.com/kelektiv/node-cron" rel="noopener noreferrer"&gt;Node Cron&lt;/a&gt;, and shell scripting for continuous deployment (but I will go into this further in my next post - &lt;a href="http://twitter.com/daviddeejjames" rel="noopener noreferrer"&gt;follow me on Twitter!&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I won't go into too much more detail about specifics of the application as you can just take a look at it &lt;a href="https://github.com/daviddeejjames/send-that-invoice" rel="noopener noreferrer"&gt;here on GitHub&lt;/a&gt;. However, I would like to go on further to explain the issues I faced, how I could improve the application, and what utilities made writing this application a joy to create!&lt;/p&gt;

&lt;h2&gt;
  
  
  Promises and the Dropbox API
&lt;/h2&gt;

&lt;p&gt;Previously, I had worked with the Dropbox API using PHP to create an &lt;a href="https://github.com/daviddeejjames/randophoto" rel="noopener noreferrer"&gt;application&lt;/a&gt; that would randomly pick a set of photos and display them onto a webpage. This was fairly basic and just didn't feel right because we were just calling the API with a curl function and I am trying to use less PHP where I can these days.&lt;/p&gt;

&lt;p&gt;When it came round to building the invoice application, I found out that Dropbox had created a &lt;a href="https://github.com/dropbox/dropbox-sdk-js" rel="noopener noreferrer"&gt;Javscript SDK&lt;/a&gt; to interact with the API. This was exciting, and even more exciting when I read the documentation to find out that it was promise based! Promises mean that you can easily chain a few API calls to get the data that you require or perform the actions you need to with little to no effort.&lt;/p&gt;

&lt;p&gt;Here is an example of a promise chain to download a file. It assumes you are passing the path of the file, which you can get easily using another API call/promise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Dropbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dropbox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Dropbox&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ACCESS_TOKEN&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dbx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filesDownload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error downloading the file ❎&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can't believe it's not butter! So simple, much file. 🐕&lt;/p&gt;

&lt;p&gt;Just to show you I'm not bluffing, I created another function that I called once the email was sent. This moves the file in Dropbox to another folder to indicate that this invoice has been sent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;archiveFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subFolderName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;archivedFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dbx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filesMove&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;from_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;to_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/sent/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;subFolderName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;allow_shared_folder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;autorename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;allow_ownership_transfer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileMove&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;File &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;fileMove&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; archived successfully! 🗳️&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fileMove&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error archiving the file 💥&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;archivedFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I pass the client name as the &lt;code&gt;subFolderName&lt;/code&gt; which means that you get a well organised file path like &lt;code&gt;/sent/client-name/INV0001.PDF&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  But what about the Email?
&lt;/h3&gt;

&lt;p&gt;Oh right, so before we go archiving the file, we obviously send the email. The creation of this email involves a few small parts but the sending of it is very straightforward.&lt;/p&gt;

&lt;p&gt;As my Mum has multiple clients, the solution needed to incorporate some form of reusability and scalability. I managed this by creating each client as a JSON file that would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Recipient"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test@email.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"An interesting Email Subject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hi John Doe,&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Invoice attached.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Kind Regards,&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Jane Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"file-prefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INV"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensured each file to be sent from Dropbox would be mailed out based on its filename prefix, allowing each client to have a different name, email, subject, or text within the email. This also means that if she ever get more clients, it is just a matter of creating new JSON files to also be a part of the automation train. 🚂&lt;/p&gt;

&lt;p&gt;Using the data above and the calls to the Dropbox API we are able to build our email and send it using &lt;a href="https://github.com/nodemailer/nodemailer" rel="noopener noreferrer"&gt;Nodemailer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code for sending an email through Nodemailer is a function call with a few option parameters (as seen below). These are passed then used in conjunction with a transport function, with most of its config set using environment variables (because you don't want people spamming you or knowing your SMTP credentials).&lt;/p&gt;

&lt;p&gt;In this application, I added the file using a binary file stream/buffer which sounds far more complicated than it is or needs to be. In reality, it just means we get the binary version of the file from Dropbox, save it as a variable, pass it to the buffer, and then it becomes a file attachment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="p"&gt;...&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendInvoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;attachedFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchFilePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePrefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;foundFilePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;attachedFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mailPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="c1"&gt;// binary buffer as an attachment&lt;/span&gt;
            &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileBinary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;binary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;binary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mailPromise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voila! There is the majority of the application in just a few function calls. If &lt;code&gt;mailPromise&lt;/code&gt; resolves, then our email will send.&lt;/p&gt;

&lt;p&gt;To test email sending while in development, using a service such as &lt;a href="https://mailtrap.io/" rel="noopener noreferrer"&gt;Mailtrap&lt;/a&gt; is a lifesaver as it is free and doesn't fill up anyone’s inboxes 😆&lt;/p&gt;

&lt;p&gt;Once I got to production, I changed it over to &lt;a href="https://www.mailgun.com/" rel="noopener noreferrer"&gt;Mailgun&lt;/a&gt; as you can send up to 10,000 emails every month for free!&lt;/p&gt;

&lt;h3&gt;
  
  
  Automation 🤖
&lt;/h3&gt;

&lt;p&gt;So it seems the application covers all your previously mentioned steps... but what makes it automatic?&lt;/p&gt;

&lt;p&gt;Not much really, just run the function once every hour (or as much as you'd like) using a &lt;a href="https://github.com/kelektiv/node-cron" rel="noopener noreferrer"&gt;cron&lt;/a&gt;. A cron is "a command to an operating system or server for a job that is to be executed at a specified time". In this case, the application checks if there are any files to be sent. If there are, execute the rest of the application; if not, don't do anything. As previously mentioned, promise chains make this process a breeze.&lt;/p&gt;

&lt;p&gt;Like everything, there is always room to improve. The cron could be removed by only running the function when a file has been uploaded, and obviously you can't just do this with the API but you smart cookies out there would've realised you can do this with the use of webhooks (but that’s for another time).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This project was super enjoyable! I learnt a multitude of things from Node to Shell scripting, from Cron jobs to Promises. Little side projects like these really push you forward as a developer. They allow for you to be the perfectionist you want to be and create something to improve your life (and sometimes others’ lives too) in more ways than one.&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Site generating with the great GatsbyJS</title>
      <dc:creator>David James</dc:creator>
      <pubDate>Sun, 01 Oct 2017 14:37:00 +0000</pubDate>
      <link>https://forem.com/daviddeejjames/site-generating-with-the-great-gatsbyjs-68e</link>
      <guid>https://forem.com/daviddeejjames/site-generating-with-the-great-gatsbyjs-68e</guid>
      <description>&lt;p&gt;When someone mentions static site generation a lot of people think ofÂ &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;. I had heard some good things about it and noticed it would allow me to build a blog quite easily. I was eager to learn something new and get something up and running that was more than just my usual WordPress theme. I was on the verge of rebuilding my portfolio site until a certain &lt;a href="https://www.youtube.com/user/LevelUpTuts"&gt;Scott Tolinski&lt;/a&gt; released a video on &lt;a href="https://www.youtube.com/watch?v=b2H7fWhQcdE&amp;amp;feature=youtu.be"&gt;GatsbyJS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.gatsbyjs.org/"&gt;GatsbyJS&lt;/a&gt; is a static site generator, similar to Jekyll, however it is written using &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; and allows you to write your pages as React components! It is somewhat similar to create-react-app where almost all the scary Webpack config has been abstracted away from you and is setup ready to go, so you can get to the important stuff like...building the site! (If you are a fan of React and not convinced, the &lt;a href="https://reactjs.org/"&gt;React website/docs&lt;/a&gt;Â were just released using Gatsby!)&lt;/p&gt;

&lt;p&gt;This caught my attention for multiple reasons. I wanted to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be able to build a new site with a blog relatively quickly&lt;/li&gt;
&lt;li&gt;Have a site that loads quickly/is performant&lt;/li&gt;
&lt;li&gt;Learn some more React and JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another reason I ended up going with Gatsby was the promise that your project could be connected to various APIs or even a CMS of your choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The magic of GraphQL and Gatsby Plugins
&lt;/h2&gt;

&lt;p&gt;Originally, following Scott's and the official Gatsby tutorial, I had it pulling my content from good ol' Markdown files. Then I saw it... too good to be true, the mention of connecting it to &lt;a href="https://wordpress.org/"&gt;WordPress&lt;/a&gt;... I have been writing WordPress themes for almost 2 years now, so this got me super excited. To have my back-end as WordPress (including &lt;a href="https://www.advancedcustomfields.com/"&gt;ACF&lt;/a&gt;) and the front-end in React, I had found the perfect place to test my front-end abilities.&lt;/p&gt;

&lt;p&gt;I was skeptical at first: would I have to parse large amounts of JSON to get the data I needed? I have never even interacted with the WordPress REST API, how will I query it?&lt;/p&gt;

&lt;p&gt;The answer... &lt;a href="http://graphql.org/"&gt;GraphQL&lt;/a&gt;. Gatsby ships with it and through an npm install of a &lt;a href="https://www.gatsbyjs.org/docs/plugins/"&gt;gatsby-source plugin&lt;/a&gt; of your choice and a tiny bit of a config, you can start querying in no time. I was amazed with how simple queries using GraphQL. You look at them and you go "Huh, that's it? Really?". Gatsby even ships with a in-browser query tester so you can see exactly what data you are getting from your GraphQL queries. Wanna sort those blog posts by date? No problem, just add a flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up and future
&lt;/h2&gt;

&lt;p&gt;In just a few weekends I managed to rebuild my portfolio site with the blog I wanted. I'd highly recommend &lt;a href="https://www.gatsbyjs.org/tutorial/"&gt;Gatsby&lt;/a&gt;Â for anyone who has started getting acquainted with React. Before I started this project I didn't know a lot about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static site generation&lt;/li&gt;
&lt;li&gt;Creating a Progressive Web App (PWA) and what qualifies as one&lt;/li&gt;
&lt;li&gt;React Router&lt;/li&gt;
&lt;li&gt;GraphQL&lt;/li&gt;
&lt;li&gt;Wordpress REST API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Moving forward I'd like to extend the project to include pagination within the blog, use &lt;a href="https://www.styled-components.com/"&gt;Styled Components&lt;/a&gt; and ensure the site scores a 90+ overall on &lt;a href="https://developers.google.com/web/tools/lighthouse/"&gt;Lighthouse&lt;/a&gt; (Google performance auditing tool)&lt;/p&gt;

&lt;p&gt;tl;dr - Side projects are great and you can learn a lot from them. Stay in the loop and listen to others whether that be watching tutorials, listening to podcasts, following devs on twitter, reading articles on &lt;a href="https://dev.to"&gt;dev.to&lt;/a&gt;, blogs etc. #neverstoplearning&lt;/p&gt;

&lt;p&gt;If you are like me, learn something, build something, even if it's small, then write about it, then you'll have a first blog post, just like this one :D&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
