Last active
June 27, 2017 21:13
-
-
Save anomaly44/663bb1e44ba8c2252e40f70bfd7c2aa3 to your computer and use it in GitHub Desktop.
Code Samples
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 _ = require('lodash'); | |
const debug = require('debug')('fixapi:utils:http'); | |
const errors = require('../../utils/error'); | |
/** | |
* # http | |
* | |
* Wrapper for all API functions. Wraps the API method so that it receives the data from the request. | |
* And formats a JSON response depending on the result or error. | |
* | |
* @public | |
* @param {Function} apiMethod The method we are wrapping | |
* @return {Function} an express middleware type function | |
*/ | |
const http = function http(apiMethod) { | |
return function apiHandler(req, res, next) { | |
// We define 2 properties for using as arguments in API calls: | |
let object = req.body; | |
let options = _.extend({}, req.file, req.query, req.params, { | |
issue: req.issue, | |
context: { | |
agencyId: (req.decoded) ? req.decoded.aId : null, | |
accountId: (req.decoded) ? req.decoded.id : null | |
} | |
}); | |
// for GET or DELETE requests there won't be a body, so we should only pass the options object to the api method | |
// for POST and PUT req.body will be an object. | |
if (_.isEmpty(object)) { | |
object = options; | |
options = {}; | |
} | |
// format the response | |
return apiMethod(object, options).then((response) => { | |
if (req.method === 'DELETE') { | |
debug('Sending 204 for succesful DELETE'); | |
return res.status(204).end(); | |
} | |
// When our API method wants to send a custom response, it can return a | |
// function to handle that | |
if (_.isFunction(response)) { | |
debug('API method response handler present'); | |
return response(req, res, next); | |
} | |
// send a properly formatted http response | |
return res.json(response || {}); | |
}) | |
// catch our custom errors, and format them according to client needs | |
// all apiMethods return bluebird promises to allow catching different errors | |
.catch(errors.UserError, err => { | |
console.error(err); | |
return res.status(500).json({ success: false, error: err.message }); | |
}) | |
.catch(errors.LogicError, err => { | |
console.error(err); | |
res.status(500).json({ success: false, error: err.message }); | |
}) | |
.catch(errors.DataError, err => { | |
console.error(err); | |
res.status(500).json({ success: false, error: err }); | |
}) | |
.catch(errors.CheckitError, err => { | |
console.error(err); | |
// only return the 1st error.message to be consistent with other errors. | |
res.status(500) | |
.json({ success: false, error: err.errors[Object.keys(err.errors)[0]].message }); | |
}); | |
}; | |
}; | |
module.exports = { http }; |
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'; | |
const debug = require('debug')('fixapi:utils:s3'); | |
const aws = require('aws-sdk'); | |
const path = require('path'); | |
const Promise = require('bluebird'); | |
const error = require('./error'); | |
const AWS_ACCESS_KEY = process.env.AWS_ACCESS_KEY; | |
const AWS_SECRET_KEY = process.env.AWS_SECRET_KEY; | |
const S3_BUCKET = process.env.S3_BUCKET; | |
const ENVIRONMENT = process.env.NODE_ENV; | |
aws.config.update({ accessKeyId: AWS_ACCESS_KEY, secretAccessKey: AWS_SECRET_KEY }); | |
const s3 = new aws.S3(); | |
/** | |
* # prependFilePathIfNeeded | |
* | |
* Make sure the files get put in correct buckets for the corresponding environments | |
* | |
* @param {String} filePath | |
* @param {String} env | |
* @returns {String} The new filePath | |
*/ | |
function prependFilePathIfNeeded(filePath, env = 'development') { | |
if (env === 'production') { | |
return filePath; | |
} else if (env === 'staging') { | |
return path.join('staging', filePath) | |
} | |
// for all other cases, use development | |
return path.join('development', filePath); | |
} | |
/** | |
* # getSignedIssueImageUrl | |
* | |
* returns a signed requestUrl to upload images to S3 directly from the browser | |
* | |
* @param {Integer} agencyId | |
* @param {Integer} issueId | |
* @param {String} fileName | |
* @param {String} [fileType="image/jpeg"] | |
* @returns {Object} Object containing signed_request and the files url | |
*/ | |
function getSignedIssueImageUrl(agencyId, issueId, fileName, fileType = 'image/jpeg') { | |
return Promise.try(() => { | |
if (!agencyId) { | |
throw new error.LogicError('No agency id provided'); | |
} | |
if (!issueId) { | |
throw new error.LogicError('No issue id provided'); | |
} | |
if (!fileName) { | |
throw new error.LogicError('No filename provided'); | |
} | |
let filePath = path.join(agencyId.toString(), issueId.toString(), fileName); | |
filePath = prependFilePathIfNeeded(filePath, ENVIRONMENT); | |
const s3Params = { | |
Bucket: S3_BUCKET, | |
Key: filePath, | |
Expires: 60, | |
ContentType: fileType, | |
ACL: 'public-read' | |
}; | |
debug(s3Params); | |
// Wrap s3 method in Promise | |
return new Promise((resolve, reject) => { | |
s3.getSignedUrl('putObject', s3Params, (err, data) => { | |
if (err) { | |
console.log(err); | |
reject('Cannot create S3 signed URL'); | |
} | |
resolve({ | |
signed_request: data, | |
url: `https://${S3_BUCKET}.s3.amazonaws.com/${filePath}` | |
}); | |
}); | |
}) | |
}) | |
} | |
/** | |
* # uploadBuffer | |
* | |
* Uploads a buffer to amazon S3 | |
* | |
* @param remoteFilePath | |
* @param buffer | |
*/ | |
function uploadBuffer(remoteFilePath, buffer) { | |
const filePath = prependFilePathIfNeeded(remoteFilePath, ENVIRONMENT); | |
debug(`Uploading file to s3: ${filePath}`); | |
// Wrap s3 method in promise | |
return new Promise((resolve, reject) => { | |
s3.putObject({ | |
ACL: 'public-read', | |
Bucket: S3_BUCKET, | |
Key: filePath, | |
Body: buffer | |
}, (error, response) => { | |
if (error) reject(error); | |
// upload succeeded | |
debug(`uploaded buffer to [ ${filePath} ]`); | |
resolve(response); | |
}); | |
}); | |
} | |
module.exports = { | |
getSignedIssueImageUrl, | |
uploadBuffer, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment