Skip to content

Instantly share code, notes, and snippets.

@alexpchin
Forked from ifraixedes/LICENSE
Created July 15, 2016 12:41
Show Gist options
  • Select an option

  • Save alexpchin/00fade76efe596baeafed86e64b951f8 to your computer and use it in GitHub Desktop.

Select an option

Save alexpchin/00fade76efe596baeafed86e64b951f8 to your computer and use it in GitHub Desktop.

Revisions

  1. @ifraixedes ifraixedes revised this gist Apr 5, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion aws-handler.js
    Original file line number Diff line number Diff line change
    @@ -56,7 +56,7 @@ exports.AwsHandler = function (event, context) {
    }
    ], function (err) {
    if (err) {
    console.error('Error processing image, detais %s', err.message);
    console.error('Error processing image, details %s', err.message);
    context.done(err);
    } else {
    context.done(null, 'Successfully resized images bucket: ' + s3Bucket + ' / key: ' + s3Key);
  2. @ifraixedes ifraixedes revised this gist Apr 5, 2015. 2 changed files with 168 additions and 3 deletions.
    169 changes: 167 additions & 2 deletions aws-handler-test.js
    Original file line number Diff line number Diff line change
    @@ -115,10 +115,175 @@ var expect = chai.expect;
    });
    });

    describe('process the image when the it has jpge extension', function () {
    var event, gmStubs, getObjectStub, putObjectStub, contextDoneSpy, testedModule, fakeResponse;

    before(function (done) {
    fakeResponse = { Body: 'image content' };
    event = extend({}, baseEvent);
    event.Records[0].s3.object.key = "image.jpge";

    gmStubs = getGmStubs();
    getObjectStub = sinon.stub().callsArgWith(1, null, fakeResponse);
    putObjectStub = sinon.stub().callsArgWith(1, null);
    contextDoneSpy = sinon.spy();

    testedModule = getTestedModule(gmStubs.gm, getObjectStub, putObjectStub);
    testedModule.AwsHandler(event, { done: function () {
    contextDoneSpy.apply(null, arguments);
    done();
    }});
    });

    it('call s3 getObject', function () {
    expect(getObjectStub).has.been.calledOnce;
    expect(getObjectStub).has.been.calledWith({
    Bucket: event.Records[0].s3.bucket.name,
    Key: event.Records[0].s3.object.key
    });
    });

    it('call graphics magick', function () {
    expect(gmStubs.gm).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.resize).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.toBuffer).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.gm).always.has.been.calledWith(fakeResponse.Body, event.Records[0].s3.object.key);

    sizesConfigs.forEach(function(s) {
    expect(gmStubs.resize).has.been.calledWith(s.width);
    expect(gmStubs.toBuffer).has.been.calledWith('jpg');
    });
    });

    it('call s3 putObject', function () {
    expect(putObjectStub).has.been.callCount(sizesConfigs.length);

    sizesConfigs.forEach(function(s) {
    expect(putObjectStub).has.been.calledWith({
    Bucket: event.Records[0].s3.bucket.name,
    Key: 'dst/' + s.destinationPath + '/' + event.Records[0].s3.object.key,
    Body: 'data',
    ContentType: 'image/jpg'
    });
    });
    });

    it('call context done with no error', function () {
    expect(contextDoneSpy).has.been.calledOnce.and.calledWith(null);
    });
    });

    describe('process the image when the it has png extension', function () {
    var event, gmStubs, getObjectStub, putObjectStub, contextDoneSpy, testedModule, fakeResponse;

    before(function (done) {
    fakeResponse = { Body: 'image content' };
    event = extend({}, baseEvent);
    event.Records[0].s3.object.key = "image.png";

    gmStubs = getGmStubs();
    getObjectStub = sinon.stub().callsArgWith(1, null, fakeResponse);
    putObjectStub = sinon.stub().callsArgWith(1, null);
    contextDoneSpy = sinon.spy();

    testedModule = getTestedModule(gmStubs.gm, getObjectStub, putObjectStub);
    testedModule.AwsHandler(event, { done: function () {
    contextDoneSpy.apply(null, arguments);
    done();
    }});
    });

    it('call s3 getObject', function () {
    expect(getObjectStub).has.been.calledOnce;
    expect(getObjectStub).has.been.calledWith({
    Bucket: event.Records[0].s3.bucket.name,
    Key: event.Records[0].s3.object.key
    });
    });

    it('call graphics magick', function () {
    expect(gmStubs.gm).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.resize).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.toBuffer).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.gm).always.has.been.calledWith(fakeResponse.Body, event.Records[0].s3.object.key);

    sizesConfigs.forEach(function(s) {
    expect(gmStubs.resize).has.been.calledWith(s.width);
    expect(gmStubs.toBuffer).has.been.calledWith('png');
    });
    });

    it('call s3 putObject', function () {
    expect(putObjectStub).has.been.callCount(sizesConfigs.length);

    sizesConfigs.forEach(function(s) {
    expect(putObjectStub).has.been.calledWith({
    Bucket: event.Records[0].s3.bucket.name,
    Key: 'dst/' + s.destinationPath + '/' + event.Records[0].s3.object.key,
    Body: 'data',
    ContentType: 'image/png'
    });
    });
    });

    it('call context done with no error', function () {
    expect(contextDoneSpy).has.been.calledOnce.and.calledWith(null);
    });
    });

    describe('process the image but image magick fails', function () {
    var event, gmStubs, getObjectStub, putObjectSpy, contextDoneSpy, testedModule, fakeResponse;

    before(function (done) {
    fakeResponse = { Body: 'image content' };
    event = extend({}, baseEvent);
    event.Records[0].s3.object.key = "image.png";

    var toBufferStub = sinon.stub().callsArgWith(1, new Error('Image resize failed'));
    gmStubs = getGmStubs(toBufferStub);
    getObjectStub = sinon.stub().callsArgWith(1, null, fakeResponse);
    putObjectSpy = sinon.spy();
    contextDoneSpy = sinon.spy();

    testedModule = getTestedModule(gmStubs.gm, getObjectStub, putObjectSpy);
    testedModule.AwsHandler(event, { done: function () {
    contextDoneSpy.apply(null, arguments);
    done();
    }});
    });

    it('call s3 getObject', function () {
    expect(getObjectStub).has.been.calledOnce;
    expect(getObjectStub).has.been.calledWith({
    Bucket: event.Records[0].s3.bucket.name,
    Key: event.Records[0].s3.object.key
    });
    });

    it('call graphics magick', function () {
    expect(gmStubs.gm).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.resize).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.toBuffer).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.gm).always.has.been.calledWith(fakeResponse.Body, event.Records[0].s3.object.key);

    sizesConfigs.forEach(function(s) {
    expect(gmStubs.resize).has.been.calledWith(s.width);
    expect(gmStubs.toBuffer).has.been.calledWith('png');
    });
    });

    it('never call s3 putObject', function () {
    expect(putObjectSpy).has.been.not.called;
    });

    it('call context done with no error', function () {
    expect(contextDoneSpy).has.been.calledOnce.and.calledWith(new Error('Image resize failed'));
    });
    });
    });

    function getGmStubs() {
    var toBuffer = sinon.stub().callsArgWith(1, null, 'data')
    function getGmStubs(toBuffer) {
    var toBuffer = toBuffer || sinon.stub().callsArgWith(1, null, 'data')
    var resize = sinon.stub().returns({ toBuffer: toBuffer });
    var gm = sinon.stub().returns({ resize: resize });

    2 changes: 1 addition & 1 deletion aws-handler.js
    Original file line number Diff line number Diff line change
    @@ -26,7 +26,7 @@ exports.AwsHandler = function (event, context) {
    return;
    }

    var imageType = imgExt[1];
    var imageType = imgExt[1] || imgExt[2];

    async.waterfall([
    function download(next) {
  3. @ifraixedes ifraixedes created this gist Apr 5, 2015.
    14 changes: 14 additions & 0 deletions LICENSE
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,14 @@
    DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Version 2, December 2004
    (http://www.wtfpl.net/about/)

    Copyright (C) 2015 Mario Mendes (@hyprstack)
    Copyright (C) 2015 Ivan Fraixedes (https://ivan.fraixed.es)

    Everyone is permitted to copy and distribute verbatim or modified
    copies of this license document, and changing it is allowed as long
    as the name is changed.

    DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

    0. You just DO WHAT THE FUCK YOU WANT TO.
    144 changes: 144 additions & 0 deletions aws-handler-test.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,144 @@
    require('blanket')({
    pattern: function (filename) {
    return !/node_modules/.test(filename);
    }
    });

    var proxyquire = require('proxyquire');
    var chai = require('chai');
    var sinonChai = require("sinon-chai");
    var extend = require('lodash').extend;
    var sinon = require('sinon');
    chai.use(sinonChai);

    describe('AwsHandler', function () {
    var expect = chai.expect;
    var sizesConfigs = [
    { width: 800, destinationPath: 'large' },
    { width: 500, destinationPath: 'medium' },
    { width: 200, destinationPath: 'small' },
    { width: 45, destinationPath: 'thumbnail'}
    ];
    var baseEvent = {
    "Records": [
    { "s3": {
    "bucket": { "name": "testbucket" },
    "object": { "key": null }
    }
    }
    ]
    };

    describe('reject to process non recognised image file extensions', function () {
    var event, gmSpy, getObjectSpy, putObjectSpy, contextDoneSpy, testedModule;

    before(function () {
    event = extend({}, baseEvent);
    event.Records[0].s3.object.key = "no-supported.gif";
    gmSpy = sinon.spy();
    getObjectSpy = sinon.spy();
    putObjectSpy = sinon.spy();
    contextDoneSpy = sinon.spy();
    testedModule = getTestedModule(gmSpy, getObjectSpy, putObjectSpy);
    testedModule.AwsHandler(event, { done: contextDoneSpy });
    });

    it('never call s3 getObject', function () {
    expect(getObjectSpy).has.not.been.called;
    });
    it('never call graphics magick', function () {
    expect(gmSpy).has.not.been.called;
    });
    it('never call s3 putObject', function () {
    expect(putObjectSpy).has.not.been.called;
    });
    it('call context done with error', function () {
    expect(contextDoneSpy).has.been.calledOnce.and.calledWith(new Error());
    });
    });

    describe('process the image when the it has jpg extension', function () {
    var event, gmStubs, getObjectStub, putObjectStub, contextDoneSpy, testedModule, fakeResponse;

    before(function (done) {
    fakeResponse = { Body: 'image content' };
    event = extend({}, baseEvent);
    event.Records[0].s3.object.key = "image.jpg";

    gmStubs = getGmStubs();
    getObjectStub = sinon.stub().callsArgWith(1, null, fakeResponse);
    putObjectStub = sinon.stub().callsArgWith(1, null);
    contextDoneSpy = sinon.spy();

    testedModule = getTestedModule(gmStubs.gm, getObjectStub, putObjectStub);
    testedModule.AwsHandler(event, { done: function () {
    contextDoneSpy.apply(null, arguments);
    done();
    }});
    });

    it('call s3 getObject', function () {
    expect(getObjectStub).has.been.calledOnce;
    expect(getObjectStub).has.been.calledWith({
    Bucket: event.Records[0].s3.bucket.name,
    Key: event.Records[0].s3.object.key
    });
    });

    it('call graphics magick', function () {
    expect(gmStubs.gm).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.resize).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.toBuffer).has.been.callCount(sizesConfigs.length);
    expect(gmStubs.gm).always.has.been.calledWith(fakeResponse.Body, event.Records[0].s3.object.key);

    sizesConfigs.forEach(function(s) {
    expect(gmStubs.resize).has.been.calledWith(s.width);
    expect(gmStubs.toBuffer).has.been.calledWith('jpg');
    });
    });

    it('call s3 putObject', function () {
    expect(putObjectStub).has.been.callCount(sizesConfigs.length);

    sizesConfigs.forEach(function(s) {
    expect(putObjectStub).has.been.calledWith({
    Bucket: event.Records[0].s3.bucket.name,
    Key: 'dst/' + s.destinationPath + '/' + event.Records[0].s3.object.key,
    Body: 'data',
    ContentType: 'image/jpg'
    });
    });
    });

    it('call context done with no error', function () {
    expect(contextDoneSpy).has.been.calledOnce.and.calledWith(null);
    });
    });

    });

    function getGmStubs() {
    var toBuffer = sinon.stub().callsArgWith(1, null, 'data')
    var resize = sinon.stub().returns({ toBuffer: toBuffer });
    var gm = sinon.stub().returns({ resize: resize });

    return {
    gm: gm,
    resize: resize,
    toBuffer: toBuffer
    };
    }

    function getTestedModule(gm, getObject, putObject) {
    return proxyquire('../aws-handler.js', {
    'gm': { subClass: function() { return gm; } },
    'aws-sdk': {
    "S3": function () {
    return {
    getObject: getObject,
    putObject: putObject
    };
    }
    }
    });
    }
    65 changes: 65 additions & 0 deletions aws-handler.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,65 @@
    'use strict';

    var s3 = new (require('aws-sdk')).S3();
    var gm = require('gm').subClass({ imageMagick: true });
    var async = require('async');

    var imageTypeRegExp = /(?:(jpg)e?|(png))$/;

    var sizesConfigs = [
    { width: 800, destinationPath: 'large' },
    { width: 500, destinationPath: 'medium' },
    { width: 200, destinationPath: 'small' },
    { width: 45, destinationPath: 'thumbnail'}
    ];

    exports.AwsHandler = function (event, context) {
    var s3Bucket = event.Records[0].s3.bucket.name;
    var s3Key = event.Records[0].s3.object.key;

    // Check if file has a supported image extension
    var imgExt = imageTypeRegExp.exec(s3Key);

    if (imgExt === null) {
    console.error('unable to infer the image type for key %s', s3Key);
    context.done(new Error('unable to infer the image type for key %s' + s3Key));
    return;
    }

    var imageType = imgExt[1];

    async.waterfall([
    function download(next) {
    s3.getObject({
    Bucket: s3Bucket,
    Key: s3Key
    }, next);
    },
    function transform(response, next) {
    async.map(sizesConfigs, function (sizeConfig, mapNext) {
    gm(response.Body, s3Key)
    .resize(sizeConfig.width)
    .toBuffer(imageType, function (err, buffer) {
    if (err) {
    mapNext(err);
    return;
    }

    s3.putObject({
    Bucket: s3Bucket,
    Key: 'dst/' + sizeConfig.destinationPath + '/' + s3Key,
    Body: buffer,
    ContentType: 'image/' + imageType
    }, mapNext)
    });
    }, next);
    }
    ], function (err) {
    if (err) {
    console.error('Error processing image, detais %s', err.message);
    context.done(err);
    } else {
    context.done(null, 'Successfully resized images bucket: ' + s3Bucket + ' / key: ' + s3Key);
    }
    });
    };
    30 changes: 30 additions & 0 deletions package.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    {
    "name": "aws-handler-image-resizer",
    "private": true,
    "version": "0.0.0",
    "description": "",
    "scripts": {
    "test": "mocha -R html-cov > coverage.html"
    },
    "author": "Mario Mendes (@hyprstack)",
    "contributors": [{
    "name": "Ivan Fraixedes",
    "email": "[email protected]",
    "url": "http://ivan.fraixed.es"
    }],
    "license": "wtfpl",
    "dependencies": {
    "async": "^0.9.0",
    "aws-sdk": "^2.1.18",
    "gm": "^1.17.0"
    },
    "devDependencies": {
    "lodash": "^3.6.0",
    "blanket": "^1.1.6",
    "mocha": "^2.2.1",
    "proxyquire": "^1.4.0",
    "sinon": "^1.14.1",
    "sinon-chai": "^2.7.0",
    "chai": "^2.1.2"
    }
    }