Skip to content

Instantly share code, notes, and snippets.

@anathematic
Created February 20, 2020 07:28
Show Gist options
  • Save anathematic/e5e119efd75af43cb7fab7a5d25f76ab to your computer and use it in GitHub Desktop.
Save anathematic/e5e119efd75af43cb7fab7a5d25f76ab to your computer and use it in GitHub Desktop.
A quick wrapper for TDD'ing through Lambda

DocMill Lambda

Lambda function to send SES received emails to DocMill API.

Tips and deployment notes

Adding a new library?

Make sure it's included in ./vendor:

bundle install --path vendor/bundle

Uploading to s3?

Zip up the app and use AWS CLI (or whatever you want) to send it to s3:

zip -r function.zip function.rb vendor
aws s3 mv function.zip s3://your-s3-url
require 'aws-sdk-core'
require 'aws-sdk'
require 'httparty'
require 'json'
require 'logger'
require 'mail'
def lambda_handler(event:, context:)
logger = Logger.new($stdout)
message = JSON.parse(event['Records'][0]['Sns']['Message'])
bucket_name = message['receipt']['action']['bucketName']
object_key = message['receipt']['action']['objectKey']
# eg: arn:aws:sns:us-east-1:104720634813:SESS3Upload
region = message['receipt']['action']['topicArn'].split(':')[3]
s3 = Aws::S3::Client.new(region: region)
s3_object = s3.get_object(bucket: bucket_name, key: object_key)
logger.info
temp_email = Tempfile.new('email.eml')
temp_email.write(s3_object.body.read)
email = Mail.read(temp_email.path)
email_attachments = email.attachments.map do |attachment|
temp = Tempfile.new(attachment.filename)
temp.write(attachment.body)
temp
end
options = {
body: {
external_document: {
document_type: 'invoice',
raw_email: File.open(temp_email.path),
attachments: email_attachments.map do |attachment_file|
File.open(attachment_file.path)
end,
metadata: {
from: email.from,
message_id: email.message_id,
reply_to: email.reply_to,
return_path: email.return_path,
sent_date: email.date.to_s,
subject: email.subject,
to: email.to
}
}
},
headers: {
"access-token" => "gFf0cBHiL5WuKqAEelL1GA",
"token-type" => "Bearer",
"client" => "tEn-yRFIUgOJgyxFC9DpzA",
"expiry" => "1583301738",
"uid" => "harry.potter@hogwarts.edu.co.uk"
}
}
api_request = HTTParty.post(
'https://ancient-beach-66221.herokuapp.com/api/v1/external_documents',
options
)
if api_request.success?
{
body: JSON.generate('Successfully sent to DocMill'),
statusCode: 201
}
else
{
body: JSON.generate(
"DocMill failed to receive the request, received #{api_request.code}"
),
statusCode: 500
}
end
end
require 'spec_helper'
require 'aws-sdk'
require 'json'
require 'httparty'
require './function.rb'
RSpec.describe 'lambda_handler' do
before do
expect_any_instance_of(Aws::S3::Client).to receive(:get_object)
.and_return(
OpenStruct.new(
body: OpenStruct.new(
read: File.read('./spec/fixtures/sample-email.eml')
)
)
)
end
subject! do
lambda_handler(
event: JSON.parse(File.read('./spec/fixtures/s3-example-event.json')),
context: nil
)
end
context 'successfully sends to DocMill' do
before do
expect(HTTParty).to receive(:post)
.and_return(
OpenStruct.new(
success?: true
)
)
end
it do
is_expected.to eq(
body: JSON.generate('Successfully sent to DocMil'),
statusCode: 201
)
end
end
context 'unsuccessfully sends to DocMill' do
before do
expect(HTTParty).to receive(:post)
.and_return(
OpenStruct.new(
code: 500,
success?: false
)
)
end
it do
is_expected.to eq(
body: JSON.generate('DocMill failed to receive the request, received 500'),
statusCode: 500
)
end
end
end
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:us-east-1:104720634813:SESS3Upload:aab9a0d2-79bc-4140-9bdb-c2029a22b0bb",
"Sns": {
"Type": "Notification",
"MessageId": "bd77840f-4a8d-5adb-96f4-d2c75791e472",
"TopicArn": "arn:aws:sns:us-east-1:104720634813:SESS3Upload",
"Subject": "Amazon SES Email Receipt Notification",
"Message": "{\"notificationType\":\"Received\",\"mail\":{\"timestamp\":\"2020-02-20T05:36:27.448Z\",\"source\":\"thomas@icdesign.com.au\",\"messageId\":\"a4ihukv3og9kbpb321fffe1kr0snb6q8m765so01\",\"destination\":[\"aws@thomassinclair.net\"],\"headersTruncated\":false,\"headers\":[{\"name\":\"Return-Path\",\"value\":\"<thomas@icdesign.com.au>\"},{\"name\":\"Received\",\"value\":\"from mail-io1-f52.google.com (mail-io1-f52.google.com [209.85.166.52]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id a4ihukv3og9kbpb321fffe1kr0snb6q8m765so01 for aws@thomassinclair.net; Thu, 20 Feb 2020 05:36:27 +0000 (UTC)\"},{\"name\":\"X-SES-Spam-Verdict\",\"value\":\"PASS\"},{\"name\":\"X-SES-Virus-Verdict\",\"value\":\"PASS\"},{\"name\":\"Received-SPF\",\"value\":\"none (spfCheck: 209.85.166.52 is neither permitted nor denied by domain of icdesign.com.au) client-ip=209.85.166.52; envelope-from=thomas@icdesign.com.au; helo=mail-io1-f52.google.com;\"},{\"name\":\"Authentication-Results\",\"value\":\"amazonses.com; spf=none (spfCheck: 209.85.166.52 is neither permitted nor denied by domain of icdesign.com.au) client-ip=209.85.166.52; envelope-from=thomas@icdesign.com.au; helo=mail-io1-f52.google.com; dkim=pass header.i=@icdesign-com-au.20150623.gappssmtp.com; dmarc=none header.from=icdesign.com.au;\"},{\"name\":\"X-SES-RECEIPT\",\"value\":\"AEFBQUFBQUFBQUFGeXE1bnBtYWdURERpWSs4NWd1NkdGNXZQMkthcVN0MHlRSElQakJDa3hURTJSaWhTZE1tTGVuTngzTFNQcjFnOHBOV0hmTTgyQmtDYnAzWjBRQ3F0b3poMXgrMWthOWtGaXU0YThTbjdQV1ozRHFQak9ndklRbThBTHRPZkI3N2w3cjE0SHEwTi8vLzRidWlYR0hMR2xoZ3pwaUovRnZZQ3pmcmpZTytWUUhvNXpIaVRlMms3UEtrK0J5SHhyYkQzelVoNU1nbzdrWTQ0K2ZFaEowbEtKSHN2VE16M3RGQ0M1Q1YwbkpiRVdRNHArZGcvNUx0NkRtcDZQTGlDNDhIajZ1Q3A3UDJWdVJ3dm9wb0d1TlhHazFOZTBGZ1RIdVMycXZ4WWpRNTVObkE9PQ==\"},{\"name\":\"X-SES-DKIM-SIGNATURE\",\"value\":\"a=rsa-sha256; q=dns/txt; b=HZ7v48TOjAatg1OXx3T3q2PEbAFhZDNzpqZcpjC6rNcuc4hewkcX8TRu/UXmw7buwtyL7WhAFY5O0TCidMAdgRpdqhk2HI3AVqYRhnUCcCrJplbzd2eXzSRUoOMCLbi/nb42UnYQ7bLsFRKfIdlNJAfJjQTtVWD1clbz8YLQeKQ=; c=relaxed/simple; s=6gbrjpgwjskckoa6a5zn6fwqkn67xbtw; d=amazonses.com; t=1582176987; v=1; bh=E9Ja8Uhnyy+9uwFbAikF555UU8SL3+Q7ZYz4q9ioAyM=; h=From:To:Cc:Bcc:Subject:Date:Message-ID:MIME-Version:Content-Type:X-SES-RECEIPT;\"},{\"name\":\"Received\",\"value\":\"by mail-io1-f52.google.com with SMTP id c16so3310276ioh.6 for <aws@thomassinclair.net>; Wed, 19 Feb 2020 21:36:27 -0800 (PST)\"},{\"name\":\"DKIM-Signature\",\"value\":\"v=1; a=rsa-sha256; c=relaxed/relaxed; d=icdesign-com-au.20150623.gappssmtp.com; s=20150623; h=mime-version:references:in-reply-to:from:date:message-id:subject:to; bh=IGwXLb069GUVEzjNT9VZNLusA/JJ+0oQCY2up9Ji1iE=; b=PLb5pJHBIrfxFqWLeIoPAN2iK6XsYzAVedHW37lkteKUjbOkKoUH416p5EnXG5HMvzlHmvFVr7IA0Ixfvvynr2d56kyaa3xxJ5ah/qpHiYTyl5iXZk8l5NJZC3VGVyYc+S8wvpgvx/CXfnu00+XieNlYSt3Lu8OdvNU02Xtca3heIPe80Qm4110Yz5KUSeyZn32qwRsch0Goe26uP/nJqpQiDFMVX3d0tQse8Esrtwz4YQxAg/TfpepdOtKofauCw81yS27ig7kb6D5mVSWlHv/1kBhe9HzGHIBL/J4q9K5vMRhVmNCc1a7sgvaQtJw7VFypWWHBK+VobD1fbNRrdw==\"},{\"name\":\"X-Google-DKIM-Signature\",\"value\":\"v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to; bh=IGwXLb069GUVEzjNT9VZNLusA/JJ+0oQCY2up9Ji1iE=; b=M1DiXv0+3JVifdbwNrKzyv8sheSGaYBbUBmdEwd5LYFuYUfUtV6wWXgf5DQ3rGKwyI GaH7cYE+H0tEP2ugrJoc7rWzcrsuNKX3OxZQ9BR4Ezkm2DCPBiAkrF5VS8ZiEXgxnuYx An6e1hbVqLNnZZJ4sNnaumtn/KxicacQXS3CKp0Xtv4UViQn28BoSE1mFeZypxDFmqti GRWaNPsk3Bx6Vebmi2tpBYLx2r3QPt2ef+vqZCSBZYh4Uh0ejpkPzAPn7pkklZ36NGsd 689pdCOA5bWS61tBr8EqPa1GJThx4qQAbGTFE/P7TfF9+4Wzuw9qwksuQuYtVGHBKqO7 Zrlw==\"},{\"name\":\"X-Gm-Message-State\",\"value\":\"APjAAAUjyNFViKnpA6PAWIzjq9HSOYmYHSGI3rQGrYLl3H27JYxz1byh R4Ey//04M7zM09fgSUPLcwXoSofULRMZ+inF9Bii51yVkQ==\"},{\"name\":\"X-Google-Smtp-Source\",\"value\":\"APXvYqyDhvRo4x6AcQ9hjhn7ii2AcucVb0s3Nkp40K9t0LepVmy1wZURPtPdx6PjWevWbrTv76mywwjczaTGaZTbdmg=\"},{\"name\":\"X-Received\",\"value\":\"by 2002:a05:6602:2113:: with SMTP id x19mr23776820iox.227.1582176985517; Wed, 19 Feb 2020 21:36:25 -0800 (PST)\"},{\"name\":\"MIME-Version\",\"value\":\"1.0\"},{\"name\":\"References\",\"value\":\"<CABBvxbFb2nQHDuL0YRixxa3ntoyOxQq_R-s9jJbaLPqAdEBFMw@mail.gmail.com>\"},{\"name\":\"In-Reply-To\",\"value\":\"<CABBvxbFb2nQHDuL0YRixxa3ntoyOxQq_R-s9jJbaLPqAdEBFMw@mail.gmail.com>\"},{\"name\":\"From\",\"value\":\"Thomas Sinclair <thomas@icdesign.com.au>\"},{\"name\":\"Date\",\"value\":\"Thu, 20 Feb 2020 16:36:12 +1100\"},{\"name\":\"Message-ID\",\"value\":\"<CABBvxbEEu4v4w14su3tqWEw05NQG-31ceyyiPRWn96vkDZv3bQ@mail.gmail.com>\"},{\"name\":\"Subject\",\"value\":\"Fwd: Another test\"},{\"name\":\"To\",\"value\":\"aws@thomassinclair.net\"},{\"name\":\"Content-Type\",\"value\":\"multipart/mixed; boundary=\\\"000000000000321454059efb499f\\\"\"}],\"commonHeaders\":{\"returnPath\":\"thomas@icdesign.com.au\",\"from\":[\"Thomas Sinclair <thomas@icdesign.com.au>\"],\"date\":\"Thu, 20 Feb 2020 16:36:12 +1100\",\"to\":[\"aws@thomassinclair.net\"],\"messageId\":\"<CABBvxbEEu4v4w14su3tqWEw05NQG-31ceyyiPRWn96vkDZv3bQ@mail.gmail.com>\",\"subject\":\"Fwd: Another test\"}},\"receipt\":{\"timestamp\":\"2020-02-20T05:36:27.448Z\",\"processingTimeMillis\":611,\"recipients\":[\"aws@thomassinclair.net\"],\"spamVerdict\":{\"status\":\"PASS\"},\"virusVerdict\":{\"status\":\"PASS\"},\"spfVerdict\":{\"status\":\"GRAY\"},\"dkimVerdict\":{\"status\":\"GRAY\"},\"dmarcVerdict\":{\"status\":\"GRAY\"},\"action\":{\"type\":\"S3\",\"topicArn\":\"arn:aws:sns:us-east-1:104720634813:SESS3Upload\",\"bucketName\":\"thomas-marketboomer-test\",\"objectKeyPrefix\":\"\",\"objectKey\":\"a4ihukv3og9kbpb321fffe1kr0snb6q8m765so01\"}}}",
"Timestamp": "2020-02-20T05:36:28.081Z",
"SignatureVersion": "1",
"Signature": "l4/eCmRAJyRu3oaOyywUeM/uqaU9cY5H2WJ+zDGXYMD0giOMv6e1HxH3c+oxWDDwN5F2e5dIonSnFMGLrY3sw++AFja9hWvqpvFTFSq0IGpHRImDB9iqdrJO7y8TgXHErGxYrT+WxobstHkHl5IgQiFDLGMt9WlHufp0CLdb1xOTv6cNutXeaN6MoN0lRKXWQozEIeUZfCD30xfhnUL5Sk28aBgp6sIXGcnB549dXvh/btz8HEdbGVEQKbkr82Sq1mizUAs9KrHVhNNFsweUYdglkSWe+p/Pyp/QggsPDBEBcjribvyUyYIhHQWIZxQ8UFVEBZCCWQRMVUDLLAmtHw==",
"SigningCertUrl": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-a86cb10b4e1f29c941702d737128f7b6.pem",
"UnsubscribeUrl": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:104720634813:SESS3Upload:aab9a0d2-79bc-4140-9bdb-c2029a22b0bb",
"MessageAttributes": {}
}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment