Skip to content

Instantly share code, notes, and snippets.

@anomaly44
Last active June 27, 2017 21:13
Show Gist options
  • Save anomaly44/663bb1e44ba8c2252e40f70bfd7c2aa3 to your computer and use it in GitHub Desktop.
Save anomaly44/663bb1e44ba8c2252e40f70bfd7c2aa3 to your computer and use it in GitHub Desktop.
Code Samples
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 };
'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