Skip to content

Instantly share code, notes, and snippets.

@dennermiranda
Created April 12, 2017 00:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dennermiranda/70963880dc95c199ecebfa5699c7abac to your computer and use it in GitHub Desktop.
Save dennermiranda/70963880dc95c199ecebfa5699c7abac to your computer and use it in GitHub Desktop.
/**
* This file contains pieces of code related to our media REST routes.
* Requesting GET media/:id returns information about a media (photo or video) with that specific identifier.
* The JSON response also includes a self signed temporal URL to acess the image or video from AWS S3,
* also another URL for the thumbnail.
*
* This endpoint is used by the app to display thumbnails or the media content that the user is currently
* browsing in the app. However, for very large albums/folders that requires the app doing a lot of requests
* while the user is browsing.
*
* Your task is to create a GET endpoint that instead of returning a single media object (including the
* temporal S3 URLs), returns an array for all of the media of a given albumId.
* (Media object schema is defined using Mongoose and contains a field called albumId set up as
* type: mongoose.Schema.Types.ObjectId)
*
* Feel free to indicate any assumptions in your response or ask us any questions. Also you can make any
* comments about the existing code of things you find good or you would improve. We are not looking for
* a specific solution, we want to know how you think.
*
* You can send your responses and comments to:
* @author José Luis Sánchez <joseluis@fotolog.com>
*/
/**
* routes/media.js
*
*/
const Joi = require('joi');
const router = require('express').Router();
const Media = require('../models/media');
const generateResponse = require('../lib/gen-response');
const s3 = require('../lib/aws-s3');
router.get('/:id', async (req, res) => {
try {
const media = await Media.findOne({ _id: req.params.id });
if (!media) return generateResponse(res, 404, { message: 'Media not found' });
if (media.userId.toString() !== req.userId)
return generateResponse(res, 401, { message: 'Not allowed' });
const getUrl = await s3.generateGetUrl(media);
generateResponse(res, 200, null, {
userId: media.userId,
type: media.type,
createdAt: media.createdAt,
s3: media.s3,
mediaTypeIndex: media.mediaTypeIndex,
metaTypeIndex: media.metaTypeIndex,
metaSubtypeIndex: media.metaSubtypeIndex,
userLocationTypeIndex: media.userLocationTypeIndex,
duration: media.duration,
date: media.date,
syncDate: media.syncDate,
location: media.location,
albumId: media.albumId,
tags: media.tags,
getMediaUrl: getUrl.mediaUrl,
getThumbUrl: getUrl.thumbUrl
})
} catch(error) {
console.log(error);
generateResponse(res, 500, error);
}
});
//My solution starts here @author Dener Miranda <denner.miranda@gmail.com>.
router.get('/albums/:id', async (req, res) => {
try{
var callback = function (err, data) {
if (err) { return console.error(err); }
else { console.log(data); }
}
const medias = await Media.find({ _albumId: req.params.id }, callback);
if (!media) return generateResponse(res, 404, { message: 'Album not found!' });
if (media.userId.toString() !== req.userId)
return generateResponse(res, 401, { message: 'Not allowed' });
var response = []
medias.forEach(function(media){
const getUrl = await s3.generateGetUrl(media);
//A possible improvement would be to create a method to generate the media and avoid repeating code
response.push({
userId: media.userId,
type: media.type,
createdAt: media.createdAt,
s3: media.s3,
mediaTypeIndex: media.mediaTypeIndex,
metaTypeIndex: media.metaTypeIndex,
metaSubtypeIndex: media.metaSubtypeIndex,
userLocationTypeIndex: media.userLocationTypeIndex,
duration: media.duration,
date: media.date,
syncDate: media.syncDate,
location: media.location,
albumId: media.albumId,
tags: media.tags,
getMediaUrl: getUrl.mediaUrl,
getThumbUrl: getUrl.thumbUrl
});
});
generateResponse(res, 200, null, response);
}catch(error){
console.log(error);
generateResponse(res, 500, error);
}
});
/**
* gen-response.js
* @param res - Express response object
* @param status - response status code
* @param error - error
* @param body - body
*/
module.exports = (res, status, error, body) => {
res.status(status);
if (error) {
res.json({ errorMessage: error.message || error });
return Promise.resolve();
}
res.json(body);
return Promise.resolve();
};
/**
* aws-s3.js
* @param res - Express response object
* @param status - response status code
* @param error - error
* @param body - body
*/
const AWS = require('aws-sdk');
const promisify = require('../lib/promisify');
const config = require('../config');
AWS.config.update(config.aws);
const s3 = new AWS.S3();
module.exports = {
generateGetUrl: (media) => {
if (!media.s3.uploaded) return Promise.resolve({
mediaUrl: 'none',
thumbUrl: 'none'
});
return Promise.all([
promisify(s3.getSignedUrl.bind(s3), 'getObject', {
Bucket: config.s3bucket,
Key: media.s3.key,
Expires: 120
}),
promisify(s3.getSignedUrl.bind(s3), 'getObject', {
Bucket: config.s3bucket,
Key: media.s3.thumbKey,
Expires: 120
})
]).then(urls => Promise.resolve({
mediaUrl: urls[0],
thumbUrl: urls[1]
}))
}
};
/**
* promisify.js
* Promisificator. Converts es5 callback function to es6 promise.
* @param method
* @param args
* @returns {Promise}
*/
let promisify = require('./promisify');
module.exports = (method, ...args) => {
return new Promise((resolve, reject) => {
return method(...args, (err, result) => {
return err ? reject(err) : resolve(result);
})
})
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment