Last active
September 10, 2020 08:59
-
-
Save nosvalds/c5d7ab17bcf095ed797200399aab538d to your computer and use it in GitHub Desktop.
DigitalHumani Tree Report Lambda Function
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Responses = { | |
_200(data = {}){ | |
return { | |
headers: { | |
'Content-Type': 'application/json', | |
'Access-Control-Allow-Methods': '*', | |
'Access-Control-Allow-Origin': '*', | |
}, | |
statusCode: 200, | |
body: JSON.stringify(data) | |
} | |
}, | |
_400(data = {}){ | |
return { | |
headers: { | |
'Content-Type': 'application/json', | |
'Access-Control-Allow-Methods': '*', | |
'Access-Control-Allow-Origin': '*', | |
}, | |
statusCode: 400, | |
body: JSON.stringify(data) | |
} | |
} | |
} | |
module.exports = Responses; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const AWS = require('aws-sdk'); | |
// offline setup | |
let options = {}; | |
if (process.env.IS_OFFLINE) { | |
options = { | |
region: "localhost", | |
endpoint: "http://localhost:8008" | |
} | |
} | |
const documentClient = new AWS.DynamoDB.DocumentClient(options); | |
const Dynamo = { | |
async get (params) { | |
const data = await documentClient | |
.get(params) | |
.promise() | |
if (!data || !data.Item){ | |
throw Error(`There was an error fetching the data from DynamoDB for parameters ${params}`) | |
} | |
console.log(data); | |
return data.Item; | |
}, | |
async query (params) { | |
const data = await documentClient | |
.query(params) | |
.promise() | |
if (!data || !data.Items){ | |
throw Error(`There was an error fetching the data from DynamoDB for parameters ${params}`) | |
} | |
console.log(data); | |
return data.Items; | |
}, | |
}; | |
module.exports = Dynamo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
// Built-in | |
const { log } = console | |
// common | |
const Responses = require('./API_Responses'); | |
const Dynamo = require('./Dynamo'); | |
// AWS resources | |
const aws = require('aws-sdk') | |
const ses = new aws.SES({region: 'us-east-1'}); | |
// libraries | |
const fs = require('fs'); | |
const { promisify } = require('util') | |
const readFile = promisify(fs.readFile) | |
const Handlebars = require('handlebars'); | |
// environment variables | |
const { ENTERPRISE_TABLE, PROJECT_TABLE, TREE_TABLE } = process.env | |
exports.handler = async event => { | |
// Input data for organisation and which month | |
let body = JSON.parse(event.body); // parse JSON object | |
// need to update this to loop on active enterprises | |
const enterpriseId = body.enterpriseId // get from the event input JSON | |
let projectId = ""; | |
let monthDate = ""; | |
// if monthDate (YYYY-MM) is passed in, use that value | |
if (body.monthDate !== "") { | |
monthDate = body.monthDate; | |
} else { // report to be run 1st of following month, so we want last month | |
let today = new Date(); | |
today.setMonth(today.getMonth() - 1); | |
let month = today.getMonth() + 1; // +1 for human readable month | |
month = month < 10 ? `0${month}` : `${month}`; // deal with padding 0 | |
monthDate = `${today.getFullYear()}-${month}`; // format YYYY-MM | |
} | |
// do some validation of enterprise ID and monthDate | |
if (!enterpriseId.match(/^[0-9a-f]{8}$/)) { | |
let error = `The "enterpriseId" must be 8 hex digit string. Got "${enterpriseId}" instead.` | |
log(error); | |
return Responses._400({message: error}); | |
} else if (!monthDate.match(/^\d{4}-\d{2}$/)) { | |
let error = `Required "month date" format is YYYY-MM. Got "${monthDate}" instead.` | |
log(error); | |
return Responses._400({message: error}); | |
} | |
// Start getting data for the email | |
const TableName = TREE_TABLE | |
const IndexName = 'myGSI' | |
const query = { | |
enterpriseId, | |
} | |
const KeyConditionExpression = | |
Object.keys(query).map(x => `#${x[0]} = :${x[0]}`).join() | |
+ ' and begins_with(created, :dt)' | |
// { "#n": "name" } | |
const ExpressionAttributeNames = Object | |
.keys(query) | |
.map(x => ({ [`#${x[0]}`] : x })) | |
.reduce((x, acc) => Object.assign(acc, x), {}) | |
// { ":n": "John" } | |
const ExpressionAttributeValues = Object | |
.keys(query) | |
.map(x => ({ [`:${x[0]}`] : query[x] })) | |
.concat({':dt': monthDate}) // Ex. '2018-11' | |
.reduce((x, acc) => Object.assign(acc, x), {}) | |
let treeResult = await Dynamo.query({ | |
TableName, | |
IndexName, | |
KeyConditionExpression, | |
ExpressionAttributeNames, | |
ExpressionAttributeValues, | |
}).catch(err => { | |
log('Error in Dynamo Query', err) | |
return null | |
}) | |
if (!treeResult){ | |
return Responses._400({message: `There was an error fetching the data for enterprise of ${enterpriseId} and monthDate of ${monthDate} from ${TableName}`}) | |
} | |
// calculate tree count per project from result | |
let report = {}; | |
let treeCount = ""; | |
// sum the tree count for each projectId | |
treeResult | |
.forEach(val => { | |
report[val.projectId] = (report[val.projectId] > 0 ? report[val.projectId] : 0 ) + val.treeCount; | |
}) | |
// get the just project IDs in an array | |
let projectIds = Object.keys(report); | |
if (projectIds.length === 1) { // only purchased with 1 project (this is the norm) | |
projectId = projectIds.join(); // get the single projectId | |
treeCount = report[projectId]; // get the treeCount for that project | |
} else { | |
let error = `Trees planted with multiple projects for Enterprise id ${enterpriseId} and monthDate ${monthDate}, email must be generated manually at this time`; | |
log(error); | |
return Responses._400({message: error}); | |
} | |
// get project details | |
let projectDetails = await Dynamo.get( | |
{ | |
TableName: PROJECT_TABLE, | |
Key: { id: projectId } | |
} | |
).catch(err => { | |
log('Error in Dynamo Get', err) | |
return null | |
}) | |
if (!projectDetails) { | |
error = "Could not retrieve project data for project: " + projectId | |
log(error) | |
return Responses._400({ error }) | |
} | |
// get enterprise details | |
let enterpriseDetails = await Dynamo.get( | |
{ | |
TableName: ENTERPRISE_TABLE, | |
Key: { id: enterpriseId } | |
} | |
).catch(err => { | |
log('Error in Dynamo Get', err) | |
return null | |
}) | |
if (!enterpriseDetails) { | |
let error = "Count not retrieve enterprise data for enterprise: " + enterpriseId | |
log(error) | |
return Responses._400({ error }) | |
} | |
// prepare the email | |
let emailHtmlTemplate = await readFile(__dirname + "/emailTemplates/monthlyTreeReportEmail.html").then((result) => { | |
if (!result) { | |
log('Unable to load HTML template'); | |
throw 'Unable to load HTML template' | |
} | |
return result | |
}).catch(err => { | |
log('Unable to load HTML template', err) | |
}) | |
// Prepare data for template placeholders | |
let emailData = { | |
"enterpriseName": enterpriseDetails.name, | |
"monthDate": monthDate, | |
"treeCount": treeCount, | |
"projectName": projectDetails.reforestationProjectCountry_en, | |
"organisationName": projectDetails.reforestationCompanyName_en, | |
"contactName": enterpriseDetails.contact.name, | |
"contactEmail": enterpriseDetails.contact["email address"], | |
"year": monthDate.slice(0,4), | |
}; | |
// Inject data into the template | |
let templateHtml = Handlebars.compile(emailHtmlTemplate.toString()); | |
let bodyHtml = templateHtml(emailData); | |
// Prepare SES Parameters | |
let params = { | |
Destination: { | |
ToAddresses: [emailData.contactEmail] | |
}, | |
Message: { | |
Body: { | |
Text: { Data: `Enterprise Name: ${emailData.enterpriseName}, Trees: ${treeCount}` | |
}, | |
Html: { | |
Data: bodyHtml | |
} | |
}, | |
Subject: { | |
Data: `Monthly Report Email for ${emailData.enterpriseName} Trees: ${treeCount}` | |
} | |
}, | |
Source: "info@DigitalHumani.com" | |
}; | |
try { | |
await ses.sendEmail(params).promise(); | |
return Responses._200({message: 'email sent'}) | |
} catch (error) { | |
console.log('error', error); | |
return Responses._400({message: 'failed to send email'}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment