From b5816639a4630790f69dfb7eb2af361151b3ceb4 Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Wed, 22 Jun 2016 22:11:56 +0200 Subject: [PATCH 1/3] chore(package): upgrade package dependencies --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index c35fdd3..fc691d8 100644 --- a/package.json +++ b/package.json @@ -38,16 +38,16 @@ }, "homepage": "https://github.com/Turistforeningen/node-s3-uploader", "devDependencies": { - "@starefossen/rand-path": "^1", - "mocha": "~2", - "jshint": "^2.8.0" + "@starefossen/rand-path": "^2.0.0", + "jshint": "^2.9.2", + "mocha": "^2.5.3" }, "dependencies": { - "async": "~1.4", - "aws-sdk": "^2.2.9", + "async": "^1.5.2", + "aws-sdk": "^2.4.1", "im-metadata": "^3.0.0", - "im-resize": "~2.3.1", - "node-uuid": "^1.4.3" + "im-resize": "^2.3.1", + "node-uuid": "^1.4.7" }, "engines": { "node": ">=0.10", From 9a41f0c050c827609e04741a703d00acf049fb2b Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Wed, 22 Jun 2016 22:12:07 +0200 Subject: [PATCH 2/3] chore(docker): upgrade Docker Compose file format --- docker-compose.yml | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 0905e99..268f021 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,12 +1,15 @@ -dev: - image: starefossen/node-imagemagick:4-6 - working_dir: /usr/src/app - volumes: - - ".:/usr/src/app" - command: "npm run watch" - env_file: .env - environment: - - NODE_ENV=development - - NPM_CONFIG_LOGLEVEL=info - - NPM_PACKAGE_CONFIG_UNSAFE_PERM=true - - NPM_CONFIG_UNSAFE_PERM=true +version: '2' + +services: + node: + image: starefossen/node-imagemagick:4-6 + working_dir: /usr/src/app + volumes: + - ".:/usr/src/app" + env_file: .env + environment: + - NODE_ENV=development + - NPM_CONFIG_LOGLEVEL=silent + - NPM_CONFIG_PROGRESS=false + - NPM_CONFIG_SPIN=false + command: npm test From 59a54405a0d4bce5d4ef9b3b454b434326445f9e Mon Sep 17 00:00:00 2001 From: Hans Kristian Flaatten Date: Thu, 23 Jun 2016 23:08:45 +0200 Subject: [PATCH 3/3] feat(package): rewritten in EcmaScript 6 Close #63 BREAKING CHANGE: `s3-uploader` package now requires Node.JS v4 or newer becuase of the usage of ES6 features. --- .eslintrc | 23 ++ .gitignore | 25 --- .jshintrc | 59 ----- .npmignore | 31 --- LICENSE | 2 +- README.md | 11 +- index.js | 191 ++++++++-------- package.json | 48 +++-- test/index.js | 432 ++++++++++++++++++------------------- test/support/env.js | 3 + utils/git_hooks/pre-commit | 26 --- wercker.yml | 43 +++- 12 files changed, 402 insertions(+), 492 deletions(-) create mode 100644 .eslintrc delete mode 100644 .jshintrc delete mode 100644 .npmignore create mode 100644 test/support/env.js delete mode 100755 utils/git_hooks/pre-commit diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..a9e25b1 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,23 @@ +# vi:syntax=json +{ + "extends": "airbnb-base", + "env": { + "mocha": true, + "node": true + }, + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "script", + "ecmaFeatures": { + "modules": false + } + }, + "rules": { + "strict": [2, "global"], + "no-underscore-dangle": ["error", { "allow": [ + "_randomPath", + "_upload" + ]}], + "no-param-reassign": ["error", { "props": false }] + } +} diff --git a/.gitignore b/.gitignore index 3f8ef12..4c49bd7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1 @@ -lib-cov -*.seed -*.log -*.csv -*.dat -*.out -*.pid -*.gz -*.swp - -pids -logs -results - -node_modules/ - .env -.DS_Store -.idea -*.iml - -*.sublime* -*.sublime-project -*.sublime-workspace -*.cache -*.mpc diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index e664c37..0000000 --- a/.jshintrc +++ /dev/null @@ -1,59 +0,0 @@ -{ - /* - * ENVIRONMENTS - * ================= - */ - - // Define globals exposed by Node.js. - "node": true, - - // Define globals exposed by Mocha unit testing framework. - "mocha": true, - - // Allow ES6. - "esnext": false, - - /* - * ENFORCING OPTIONS - * ================= - */ - - // Force all variable names to use either camelCase style or UPPER_CASE - // with underscores. - "camelcase": true, - - // Prohibit use of == and != in favor of === and !==. - "eqeqeq": true, - - // Enforce tab width of 2 spaces. - "indent": 2, - - // Prohibit use of a variable before it is defined. - "latedef": true, - - // Enforce line length to 80 characters - "maxlen": 80, - - // Require capitalized names for constructor functions. - "newcap": true, - - // Enforce use of single quotation marks for strings. - "quotmark": "single", - - // Enforce placing 'use strict' at the top function scope - "strict": true, - - // Prohibit use of explicitly undeclared variables. - "undef": true, - - // Warn when variables are defined but never used. - "unused": true, - - /* - * RELAXING OPTIONS - * ================= - */ - - // Suppress warnings about == null comparisons. - "eqnull": true -} diff --git a/.npmignore b/.npmignore deleted file mode 100644 index b23b501..0000000 --- a/.npmignore +++ /dev/null @@ -1,31 +0,0 @@ -lib-cov -*.seed -*.log -*.csv -*.dat -*.out -*.pid -*.gz -*.swp - -pids -logs -results - -node_modules/ -test/ -utils/ - -docker-compose.yml -wercker.yml - -.gitignore -.npmignore -.jshintrc -.DS_Store -.idea -*.iml - -*.sublime* -*.sublime-project -*.sublime-workspace diff --git a/LICENSE b/LICENSE index d6037ef..b4968e1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2015 Den Norske Turistforening (DNT), Hans Kristian Flaatten +Copyright (c) 2014-2016 Den Norske Turistforening (DNT), Hans Kristian Flaatten Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 9062d90..f9da11c 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,13 @@ AWS S3 Image Uploader **Documentation for `s3-uploader@1.1` can be found [here](https://github.com/Turistforeningen/node-s3-uploader/blob/stable/1.x/README.md).** -[![Build status](https://img.shields.io/wercker/ci/54f18246d9b14636634ff908.svg "Build status")](https://app.wercker.com/project/bykey/50fbdf51cf64b01a738379a028b8a885) +[![Build status](https://app.wercker.com/status/9c42c66dcef6429b52989b2a61c85c7b/s "wercker status")](https://app.wercker.com/project/bykey/9c42c66dcef6429b52989b2a61c85c7b) +[![Codacy grade](https://img.shields.io/codacy/grade/17ede08ebf51447296922d6f2b1ee83c.svg "Codacy grade")](https://www.codacy.com/app/DNT/node-s3-uploader) +[![Codacy coverage](https://img.shields.io/codacy/coverage/17ede08ebf51447296922d6f2b1ee83c.svg "Codacy coverage")](https://www.codacy.com/app/DNT/node-s3-uploader) [![NPM downloads](https://img.shields.io/npm/dm/s3-uploader.svg "NPM downloads")](https://www.npmjs.com/package/s3-uploader) [![NPM version](https://img.shields.io/npm/v/s3-uploader.svg "NPM version")](https://www.npmjs.com/package/s3-uploader) [![Node version](https://img.shields.io/node/v/s3-uploader.svg "Node version")](https://www.npmjs.com/package/s3-uploader) -[![Dependency status](https://img.shields.io/david/turistforeningen/node-s3-uploader.svg "Dependency status")](https://david-dm.org/turistforeningen/node-s3-uploader) +[![Dependency status](https://img.shields.io/david/Turistforeningen/node-s3-uploader.svg "Dependency status")](https://david-dm.org/Turistforeningen/node-s3-uploader) [![Join the chat](https://img.shields.io/badge/gitter-join%20chat-blue.svg "Join the chat")](https://gitter.im/Turistforeningen/node-s3-uploader) Flexible and efficient image resize, rename, and upload to Amazon S3 disk @@ -31,11 +33,8 @@ npm install s3-uploader --save ## Requirements -* Node.JS >= v0.10 +* Node.JS >= v4.0.0 * ImageMagic >= v6.8 -* AWS credentials environment variables - * `AWS_ACCESS_KEY_ID` - * `AWS_SECRET_ACCESS_KEY` ## API diff --git a/index.js b/index.js index 2570926..4c39d80 100644 --- a/index.js +++ b/index.js @@ -1,69 +1,18 @@ 'use strict'; -var fs = require('fs'); -var extname = require('path').extname; -var S3 = require('aws-sdk').S3; +const fs = require('fs'); +const extname = require('path').extname; +const S3 = require('aws-sdk').S3; -var auto = require('async').auto; -var each = require('async').each; -var map = require('async').map; +const auto = require('async').auto; +const each = require('async').each; +const map = require('async').map; +const uuid = require('node-uuid'); -var resize = require('im-resize'); -var metadata = require('im-metadata'); +const resize = require('im-resize'); +const metadata = require('im-metadata'); -var Upload, Image; - -Upload = module.exports = function(bucketName, opts) { - this.opts = opts || {}; - - if (!bucketName) { - throw new TypeError('Bucket name can not be undefined'); - } - - if (!this.opts.aws) { this.opts.aws = {}; } - if (!this.opts.aws.acl) { this.opts.aws.acl = 'private'; } - - if (!this.opts.aws.httpOptions) { this.opts.aws.httpOptions = {}; } - if (!this.opts.aws.httpOptions.timeout) { - this.opts.aws.httpOptions.timeout = 10000; - } - - if (!this.opts.aws.maxRetries) { this.opts.aws.maxRetries = 3; } - if (!this.opts.aws.params) { this.opts.aws.params = {}; } - this.opts.aws.params.Bucket = bucketName; - if (!this.opts.aws.path) { this.opts.aws.path = ''; } - if (!this.opts.aws.region) { this.opts.aws.region = 'us-east-1'; } - if (!this.opts.aws.sslEnabled) { this.opts.aws.sslEnabled = true; } - - if (!this.opts.cleanup) { this.opts.cleanup = {}; } - if (!this.opts.returnExif) { this.opts.returnExif = false; } - - if (!this.opts.resize) { this.opts.resize = {}; } - if (!this.opts.resize.quality) { this.opts.resize.quality = 70; } - - if (!this.opts.versions) { this.opts.versions = []; } - - if (!this.opts.url && this.opts.aws.region === 'us-east-1') { - this.opts.url = 'https://s3.amazonaws.com/' + bucketName + '/'; - } else if (!this.opts.url) { - this.opts.url = [ - 'https://s3-', this.opts.aws.region, '.amazonaws.com/', bucketName, '/' - ].join(''); - } - - this._randomPath = this.opts.randomPath || require('node-uuid'); - this.s3 = new S3(this.opts.aws); - - return this; -}; - -Upload.prototype.upload = function(src, opts, cb) { - var image = new Image(src, opts, this); - - image.start(cb); -}; - -Image = module.exports.Image = function(src, opts, upload) { +const Image = function Image(src, opts, upload) { this.src = src; this.opts = opts; this.upload = upload; @@ -71,46 +20,44 @@ Image = module.exports.Image = function(src, opts, upload) { return this; }; -Image.prototype.start = function(cb) { +Image.prototype.start = function start(cb) { auto({ metadata: this.getMetadata.bind(this, this.src), dest: this.getDest.bind(this), versions: ['metadata', this.resizeVersions.bind(this)], uploads: ['versions', 'dest', this.uploadVersions.bind(this)], - cleanup: ['uploads', this.removeVersions.bind(this)] - }, function(err, results) { + cleanup: ['uploads', this.removeVersions.bind(this)], + }, (err, results) => { cb(err, results.uploads, results.metadata); }); }; -Image.prototype.getMetadata = function(src, cb) { +Image.prototype.getMetadata = function getMetadata(src, cb) { metadata(src, { exif: this.upload.opts.returnExif, - autoOrient: true + autoOrient: true, }, cb); }; -Image.prototype.getDest = function(cb) { - var prefix = this.opts.awsPath || this.upload.opts.aws.path; - var path = this.opts.path || this.upload._randomPath(); +Image.prototype.getDest = function getDest(cb) { + const prefix = this.opts.awsPath || this.upload.opts.aws.path; + const path = this.opts.path || this.upload._randomPath(); - process.nextTick(function() { - cb(null, prefix + path); - }); + process.nextTick(() => cb(null, prefix + path)); }; -Image.prototype.resizeVersions = function(cb, results) { +Image.prototype.resizeVersions = function resizeVersions(cb, results) { resize(results.metadata, { path: this.upload.opts.resize.path, prefix: this.upload.opts.resize.prefix, quality: this.upload.opts.resize.quality, - versions: JSON.parse(JSON.stringify(this.upload.opts.versions)) + versions: JSON.parse(JSON.stringify(this.upload.opts.versions)), }, cb); }; -Image.prototype.uploadVersions = function(cb, results) { +Image.prototype.uploadVersions = function uploadVersions(cb, results) { if (this.upload.opts.original) { - var org = JSON.parse(JSON.stringify(this.upload.opts.original)); + const org = JSON.parse(JSON.stringify(this.upload.opts.original)); org.original = true; org.width = results.metadata.width; @@ -123,36 +70,30 @@ Image.prototype.uploadVersions = function(cb, results) { map(results.versions, this._upload.bind(this, results.dest), cb); }; -Image.prototype.removeVersions = function(cb, results) { - var $this = this; - - each(results.uploads, function(image, callback) { - if (!$this.upload.opts.cleanup.original && image.original || - !$this.upload.opts.cleanup.versions && !image.original) - { +Image.prototype.removeVersions = function removeVersions(cb, results) { + each(results.uploads, (image, callback) => { + if (!this.upload.opts.cleanup.original && image.original || + !this.upload.opts.cleanup.versions && !image.original + ) { return setTimeout(callback, 0); } return fs.unlink(image.path, callback); - }, function() { - cb(); - }); + }, cb); }; -Image.prototype._upload = function(dest, version, cb) { - var $this = this; - +Image.prototype._upload = function _upload(dest, version, cb) { if (version.awsImageAcl == null) { version.awsImageAcl = this.upload.opts.aws.acl; } - var format = extname(version.path).substr(1).toLowerCase(); + const format = extname(version.path).substr(1).toLowerCase(); - var options = { - Key: '' + dest + (version.suffix || '') + '.' + format, + const options = { + Key: `${dest}${version.suffix || ''}.${format}`, ACL: version.awsImageAcl, Body: fs.createReadStream(version.path), - ContentType: 'image/' + (format === 'jpg' ? 'jpeg' : format) + ContentType: `image/${format === 'jpg' ? 'jpeg' : format}`, }; if (version.awsImageExpires) { @@ -160,19 +101,73 @@ Image.prototype._upload = function(dest, version, cb) { } if (version.awsImageMaxAge) { - options.CacheControl = 'public, max-age=' + version.awsImageMaxAge; + options.CacheControl = `public, max-age=${version.awsImageMaxAge}`; } - this.upload.s3.putObject(options, function(err, data) { + this.upload.s3.putObject(options, (err, data) => { if (err) { return cb(err); } version.etag = data.ETag; version.key = options.Key; - if ($this.upload.opts.url) { - version.url = $this.upload.opts.url + options.Key; + if (this.upload.opts.url) { + version.url = this.upload.opts.url + options.Key; } - cb(null, version); + return cb(null, version); }); }; + +const Upload = function Upload(bucketName, opts) { + this.opts = opts || {}; + + if (!bucketName) { + throw new TypeError('Bucket name can not be undefined'); + } + + if (!this.opts.aws) { this.opts.aws = {}; } + if (!this.opts.aws.acl) { this.opts.aws.acl = 'private'; } + + if (!this.opts.aws.httpOptions) { this.opts.aws.httpOptions = {}; } + if (!this.opts.aws.httpOptions.timeout) { + this.opts.aws.httpOptions.timeout = 10000; + } + + if (!this.opts.aws.maxRetries) { this.opts.aws.maxRetries = 3; } + if (!this.opts.aws.params) { this.opts.aws.params = {}; } + this.opts.aws.params.Bucket = bucketName; + if (!this.opts.aws.path) { this.opts.aws.path = ''; } + if (!this.opts.aws.region) { this.opts.aws.region = 'us-east-1'; } + if (!this.opts.aws.sslEnabled) { this.opts.aws.sslEnabled = true; } + + if (!this.opts.cleanup) { this.opts.cleanup = {}; } + if (!this.opts.returnExif) { this.opts.returnExif = false; } + + if (!this.opts.resize) { this.opts.resize = {}; } + if (!this.opts.resize.quality) { this.opts.resize.quality = 70; } + + if (!this.opts.versions) { this.opts.versions = []; } + + if (!this.opts.url && this.opts.aws.region === 'us-east-1') { + this.opts.url = `https://s3.amazonaws.com/${bucketName}/`; + } else if (!this.opts.url) { + this.opts.url = [ + 'https://s3-', this.opts.aws.region, + '.amazonaws.com/', bucketName, '/', + ].join(''); + } + + this._randomPath = this.opts.randomPath || uuid; + this.s3 = new S3(this.opts.aws); + + return this; +}; + +Upload.prototype.upload = function upload(src, opts, cb) { + const image = new Image(src, opts, this); + + image.start(cb); +}; + +module.exports = Upload; +module.exports.Image = Image; diff --git a/package.json b/package.json index fc691d8..cacf5c4 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,22 @@ { "name": "s3-uploader", - "version": "2.0.0-rc.1", + "version": null, "description": "Resize, rename, and upload images to AWS S3", - "main": "lib/index.js", - "directories": { - "test": "test" - }, + "main": "index.js", + "files": [ + "index.js", + "test" + ], "scripts": { - "hint": "jshint index.js test", - "test": "mocha test", - "watch": "mocha -bcw --check-leaks -R progress test" + "codacy-coverage": "codacy-coverage", + "cover": "istanbul cover --report lcovonly ./node_modules/.bin/_mocha -- -r test/support/env test/**", + "lint": "eslint index.js test", + "nsp": "nsp check", + "semantic-release": "semantic-release", + "start": "supervisor index.js", + "test": "mocha -b -c --check-leaks -R tap -r test/support/env test/**", + "test:watch": "mocha -w -b -c --check-leaks -R progress -r test/support/env test/**", + "greenkeeper-postpublish": "greenkeeper-postpublish" }, "repository": { "type": "git", @@ -25,9 +32,10 @@ "upload", "versions" ], + "author": "Den Norske Turistforening (DNT) ", "contributors": [ "HÃ¥vard Ranum ", - "Hans Kristian Flaatten ", + "Hans Kristian Flaatten ", "Anthony Ringoet ", "Luiz Freneda ", "Oleksii Sribnyi " @@ -36,12 +44,7 @@ "bugs": { "url": "https://github.com/Turistforeningen/node-s3-uploader/issues" }, - "homepage": "https://github.com/Turistforeningen/node-s3-uploader", - "devDependencies": { - "@starefossen/rand-path": "^2.0.0", - "jshint": "^2.9.2", - "mocha": "^2.5.3" - }, + "homepage": "https://github.com/Turistforeningen/node-s3-uploader#readme", "dependencies": { "async": "^1.5.2", "aws-sdk": "^2.4.1", @@ -49,8 +52,19 @@ "im-resize": "^2.3.1", "node-uuid": "^1.4.7" }, + "devDependencies": { + "@starefossen/rand-path": "^2.0.0", + "codacy-coverage": "^1.1.3", + "eslint": "^2.11.0", + "eslint-config-airbnb-base": "^3.0.1", + "eslint-plugin-import": "^1.8.1", + "greenkeeper-postpublish": "^1.0.0", + "istanbul": "^0.4.3", + "mocha": "^2.5.3", + "nsp": "^2.4.0", + "semantic-release": "^4.3.5" + }, "engines": { - "node": ">=0.10", - "iojs": ">=1.0.0" + "node": ">=4.0.0" } } diff --git a/test/index.js b/test/index.js index 1f9fc91..22b3e30 100644 --- a/test/index.js +++ b/test/index.js @@ -1,25 +1,33 @@ 'use strict'; -var assert = require('assert'); -var Upload = require('../.'); +const assert = require('assert'); +const Upload = require('../.'); -var upload = null; -var cleanup = []; +const fs = require('fs'); +const S3 = require('aws-sdk').S3; +const ReadStream = require('fs').ReadStream; +const statSync = require('fs').statSync; +const unlinkSync = require('fs').unlinkSync; -beforeEach(function() { +const randomPath = require('@starefossen/rand-path'); + +let upload; +let cleanup = []; + +beforeEach(() => { upload = new Upload(process.env.AWS_BUCKET_NAME, { aws: { path: process.env.AWS_BUCKET_PATH, region: process.env.AWS_BUCKET_REGION, - acl: 'public-read' + acl: 'public-read', }, cleanup: { versions: true, - original: false + original: false, }, original: { awsImageAcl: 'private', - awsImageMaxAge: 31536000 + awsImageMaxAge: 31536000, }, versions: [ { @@ -27,187 +35,180 @@ beforeEach(function() { maxWidth: 1040, format: 'jpg', suffix: '-large', - quality: 80 + quality: 80, }, { maxWidth: 780, aspect: '3:2!h', - suffix: '-medium' + suffix: '-medium', }, { maxWidth: 320, aspect: '16:9!h', - suffix: '-small' + suffix: '-small', }, { maxHeight: 100, aspect: '1:1', format: 'png', suffix: '-thumb1', awsImageExpires: 31536000, - cacheControl: 31536000 + cacheControl: 31536000, }, { maxHeight: 250, maxWidth: 250, aspect: '1:1', suffix: '-thumb2', awsImageExpires: 31536000, - cacheControl: 31536000 - } - ] + cacheControl: 31536000, + }, + ], }); if (process.env.INTEGRATION_TEST !== 'true') { - upload.s3.listObjects = function(path, cb) { - process.nextTick(function() { - cb(null, { - Contents: [] - }); + upload.s3.listObjects = (path, cb) => { + process.nextTick(() => { + cb(null, { Contents: [] }); }); }; - upload.s3.putObject = function(opts, cb) { - process.nextTick(function() { - cb(null, { - ETag: '"f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1"' - }); + upload.s3.putObject = (opts, cb) => { + process.nextTick(() => { + cb(null, { ETag: '"f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1"' }); }); }; } }); if (process.env.INTEGRATION_TEST === 'true') { - afterEach(function(done) { + afterEach(function afterEach(done) { this.timeout(40000); + if (cleanup.length === 0) { return process.nextTick(done); } - upload.s3.deleteObjects({ + + return upload.s3.deleteObjects({ Delete: { - Objects: cleanup - } - }, function(err) { - if (err) { - throw err; - } + Objects: cleanup, + }, + }, (err) => { + if (err) { throw err; } cleanup = []; done(); }); }); } -describe('Upload', function() { - describe('constructor', function() { - it('throws error for missing awsBucketName param', function() { - assert.throws(function() { - new Upload(); +describe('Upload', () => { + describe('constructor', () => { + it('throws error for missing awsBucketName param', () => { + assert.throws(() => { + const client = new Upload(); + assert.equal(client, null); }, /Bucket name can not be undefined/); }); - it('sets default values if not provided', function() { + + it('sets default values if not provided', () => { upload = new Upload('myBucket'); - assert(upload.s3 instanceof require('aws-sdk').S3); + assert(upload.s3 instanceof S3); assert.deepEqual(upload.opts, { aws: { acl: 'private', httpOptions: { - timeout: 10000 + timeout: 10000, }, maxRetries: 3, params: { - Bucket: 'myBucket' + Bucket: 'myBucket', }, path: '', region: 'us-east-1', - sslEnabled: true + sslEnabled: true, }, cleanup: {}, returnExif: false, resize: { - quality: 70 + quality: 70, }, versions: [], - url: 'https://s3.amazonaws.com/myBucket/' + url: 'https://s3.amazonaws.com/myBucket/', }); }); - it('sets default url based on AWS region', function() { + it('sets default url based on AWS region', () => { upload = new Upload('b', { aws: { - region: 'my-region-1' - } + region: 'my-region-1', + }, }); assert.equal(upload.opts.url, 'https://s3-my-region-1.amazonaws.com/b/'); }); - it('sets custom url', function() { + + it('sets custom url', () => { upload = new Upload('b', { - url: 'http://cdn.app.com/' + url: 'http://cdn.app.com/', }); assert.equal(upload.opts.url, 'http://cdn.app.com/'); }); - it('connects to AWS S3 using environment variables', function(done) { + + it('connects to AWS S3 using environment constiables', function it(done) { this.timeout(10000); upload = new Upload(process.env.AWS_BUCKET_NAME); upload.s3.headBucket(upload.opts.aws.params, done); }); - it('connects to AWS S3 using constructor options', function(done) { + + it('connects to AWS S3 using constructor options', function it(done) { this.timeout(10000); upload = new Upload(process.env.AWS_BUCKET_NAME, { aws: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY - } + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, }); upload.s3.headBucket(upload.opts.aws.params, done); }); }); - describe('#_randomPath()', function() { - it('returns a new random path', function() { - var path = upload._randomPath(); + describe('#_randomPath()', () => { + it('returns a new random path', () => { + const path = upload._randomPath(); assert(/^\w+(-\w+){4}$/.test(path)); }); - it('returns custom random path', function() { - var upload = new Upload(process.env.AWS_BUCKET_NAME, { - randomPath: require('@starefossen/rand-path') - }); + it('returns custom random path', () => { + upload = new Upload(process.env.AWS_BUCKET_NAME, { randomPath }); - var path = upload._randomPath(); + const path = upload._randomPath(); assert(/^\w{2}(\/\w{2}){2}$/.test(path)); }); }); }); -describe('Image', function() { - var image; +describe('Image', () => { + let image; - beforeEach(function() { - image = new Upload.Image(__dirname + '/assets/photo.jpg', {}, upload); - image.upload._randomPath = function() { - return '110ec58a-a0f2-4ac4-8393-c866d813b8d1'; - }; + beforeEach(() => { + image = new Upload.Image(`${__dirname}/assets/photo.jpg`, {}, upload); + image.upload._randomPath = () => '110ec58a-a0f2-4ac4-8393-c866d813b8d1'; }); - describe('constructor', function() { - it('sets default values', function() { + describe('constructor', () => { + it('sets default values', () => { assert(image instanceof Upload.Image); - assert.equal(image.src, __dirname + '/assets/photo.jpg'); + assert.equal(image.src, `${__dirname}/assets/photo.jpg`); assert.deepEqual(image.opts, {}); assert(image.upload instanceof Upload); }); }); - describe('#_upload()', function() { - beforeEach(function() { - image.upload.s3.putObject = function(opts, cb) { - process.nextTick(function() { - cb(null, { - ETag: '"f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1"' - }); + describe('#_upload()', () => { + beforeEach(() => { + image.upload.s3.putObject = (opts, cb) => { + process.nextTick(() => { + cb(null, { ETag: '"f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1"' }); }); }; }); - it('sets upload key', function(done) { - var version = { - path: '/some/image.jpg' - }; + it('sets upload key', done => { + const version = { path: '/some/image.jpg' }; - image.upload.s3.putObject = function(opts) { + image.upload.s3.putObject = (opts) => { assert.equal(opts.Key, '110ec58a-a0f2-4ac4-8393-c866d813b8d1.jpg'); done(); }; @@ -215,14 +216,14 @@ describe('Image', function() { image._upload('110ec58a-a0f2-4ac4-8393-c866d813b8d1', version); }); - it('sets upload key suffix', function(done) { - var version = { + it('sets upload key suffix', done => { + const version = { path: '/some/image.jpg', - suffix: '-small' + suffix: '-small', }; - image.upload.s3.putObject = function(opts) { - var dest = '110ec58a-a0f2-4ac4-8393-c866d813b8d1-small.jpg'; + image.upload.s3.putObject = (opts) => { + const dest = '110ec58a-a0f2-4ac4-8393-c866d813b8d1-small.jpg'; assert.equal(opts.Key, dest); done(); }; @@ -230,12 +231,12 @@ describe('Image', function() { image._upload('110ec58a-a0f2-4ac4-8393-c866d813b8d1', version); }); - it('sets upload key format', function(done) { - var version = { - path: '/some/image.png' + it('sets upload key format', done => { + const version = { + path: '/some/image.png', }; - image.upload.s3.putObject = function(opts) { + image.upload.s3.putObject = (opts) => { assert.equal(opts.Key, '110ec58a-a0f2-4ac4-8393-c866d813b8d1.png'); done(); }; @@ -243,12 +244,12 @@ describe('Image', function() { image._upload('110ec58a-a0f2-4ac4-8393-c866d813b8d1', version); }); - it('sets default ACL', function(done) { - var version = { - path: '/some/image.png' + it('sets default ACL', done => { + const version = { + path: '/some/image.png', }; - image.upload.s3.putObject = function(opts) { + image.upload.s3.putObject = (opts) => { assert.equal(opts.ACL, upload.opts.aws.acl); done(); }; @@ -256,13 +257,13 @@ describe('Image', function() { image._upload('110ec58a-a0f2-4ac4-8393-c866d813b8d1', version); }); - it('sets specific ACL', function(done) { - var version = { + it('sets specific ACL', done => { + const version = { path: '/some/image.png', - awsImageAcl: 'private' + awsImageAcl: 'private', }; - image.upload.s3.putObject = function(opts) { + image.upload.s3.putObject = (opts) => { assert.equal(opts.ACL, version.awsImageAcl); done(); }; @@ -270,13 +271,13 @@ describe('Image', function() { image._upload('110ec58a-a0f2-4ac4-8393-c866d813b8d1', version); }); - it('sets upload body', function(done) { - var version = { - path: '/some/image.png' + it('sets upload body', done => { + const version = { + path: '/some/image.png', }; - image.upload.s3.putObject = function(opts) { - assert(opts.Body instanceof require('fs').ReadStream); + image.upload.s3.putObject = (opts) => { + assert(opts.Body instanceof ReadStream); assert.equal(opts.Body.path, version.path); done(); }; @@ -284,12 +285,12 @@ describe('Image', function() { image._upload('110ec58a-a0f2-4ac4-8393-c866d813b8d1', version); }); - it('sets upload content type for png', function(done) { - var version = { - path: '/some/image.png' + it('sets upload content type for png', done => { + const version = { + path: '/some/image.png', }; - image.upload.s3.putObject = function(opts) { + image.upload.s3.putObject = (opts) => { assert.equal(opts.ContentType, 'image/png'); done(); }; @@ -297,12 +298,12 @@ describe('Image', function() { image._upload('110ec58a-a0f2-4ac4-8393-c866d813b8d1', version); }); - it('sets upload content type for jpg', function(done) { - var version = { - path: '/some/image.jpg' + it('sets upload content type for jpg', done => { + const version = { + path: '/some/image.jpg', }; - image.upload.s3.putObject = function(opts) { + image.upload.s3.putObject = (opts) => { assert.equal(opts.ContentType, 'image/jpeg'); done(); }; @@ -310,13 +311,13 @@ describe('Image', function() { image._upload('110ec58a-a0f2-4ac4-8393-c866d813b8d1', version); }); - it('sets upload expire header for version', function(done) { - var version = { + it('sets upload expire header for version', done => { + const version = { path: '/some/image.jpg', - awsImageExpires: 1234 + awsImageExpires: 1234, }; - image.upload.s3.putObject = function(opts) { + image.upload.s3.putObject = (opts) => { assert(opts.Expires - Date.now() <= 1234); done(); }; @@ -324,13 +325,13 @@ describe('Image', function() { image._upload('110ec58a-a0f2-4ac4-8393-c866d813b8d1', version); }); - it('sets upload cache-control header for version', function(done) { - var version = { + it('sets upload cache-control header for version', done => { + const version = { path: '/some/image.jpg', - awsImageMaxAge: 1234 + awsImageMaxAge: 1234, }; - image.upload.s3.putObject = function(opts) { + image.upload.s3.putObject = (opts) => { assert.equal(opts.CacheControl, 'public, max-age=1234'); done(); }; @@ -338,37 +339,37 @@ describe('Image', function() { image._upload('110ec58a-a0f2-4ac4-8393-c866d813b8d1', version); }); - it('returns etag for uploaded version', function(done) { - var version = { - path: '/some/image.jpg' + it('returns etag for uploaded version', done => { + const version1 = { + path: '/some/image.jpg', }; - var dest = '110ec58a-a0f2-4ac4-8393-c866d813b8d1'; - image._upload(dest, version, function(err, version) { + const dest = '110ec58a-a0f2-4ac4-8393-c866d813b8d1'; + image._upload(dest, version1, (err, version2) => { assert.ifError(err); - assert.equal(version.etag, '"f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1"'); + assert.equal(version2.etag, '"f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1"'); done(); }); }); - it('returns url for uploaded version', function(done) { - var version = { - path: '/some/image.jpg' + it('returns url for uploaded version', done => { + const version1 = { + path: '/some/image.jpg', }; - var dest = '110ec58a-a0f2-4ac4-8393-c866d813b8d1'; - image._upload(dest, version, function(err, version) { + const dest = '110ec58a-a0f2-4ac4-8393-c866d813b8d1'; + image._upload(dest, version1, (err, version2) => { assert.ifError(err); - assert.equal(version.url, image.upload.opts.url + dest + '.jpg'); + assert.equal(version2.url, `${image.upload.opts.url}${dest}.jpg`); done(); }); }); }); - describe('#getMetadata()', function() { - it('returns image metadata without exif data', function(done) { + describe('#getMetadata()', () => { + it('returns image metadata without exif data', done => { image.upload.opts.returnExif = false; - image.getMetadata(image.src, function(err, metadata) { + image.getMetadata(image.src, (err, metadata) => { assert.ifError(err); assert.deepEqual(metadata, { path: image.src, @@ -378,15 +379,15 @@ describe('Image', function() { colorspace: 'RGB', height: 2048, width: 1536, - orientation: '' + orientation: '', }); done(); }); }); - it('returns image metadata with exif data', function(done) { + it('returns image metadata with exif data', done => { image.upload.opts.returnExif = true; - image.getMetadata(image.src, function(err, metadata) { + image.getMetadata(image.src, (err, metadata) => { assert.ifError(err); assert.equal(Object.keys(metadata).length, 9); assert.equal(metadata.exif.GPSInfo, '954'); @@ -395,39 +396,39 @@ describe('Image', function() { }); }); - describe('#getDest()', function() { - it('returns destination path', function(done) { - var dest = '110ec58a-a0f2-4ac4-8393-c866d813b8d1'; + describe('#getDest()', () => { + it('returns destination path', done => { + const dest = '110ec58a-a0f2-4ac4-8393-c866d813b8d1'; - image.getDest(function(err, path) { + image.getDest((err, path) => { assert.ifError(err); assert.equal(path, image.upload.opts.aws.path + dest); done(); }); }); - it('overrides destination path prefix', function(done) { + it('overrides destination path prefix', done => { image.opts.awsPath = 'custom/path/'; - image.getDest(function(err, path) { + image.getDest((err, path) => { assert.ifError(err); assert.equal(path, 'custom/path/110ec58a-a0f2-4ac4-8393-c866d813b8d1'); done(); }); }); - it('returns fixed upload path', function(done) { + it('returns fixed upload path', done => { image.opts.path = 'my/image'; - image.getDest(function(err, path) { + image.getDest((err, path) => { assert.ifError(err); assert.equal(path, 'images_test/my/image'); done(); }); }); - it('returns fixed upload path with custom prefix', function(done) { + it('returns fixed upload path with custom prefix', done => { image.opts.awsPath = 'custom/path/'; image.opts.path = 'my/image'; - image.getDest(function(err, path) { + image.getDest((err, path) => { assert.ifError(err); assert.equal(path, 'custom/path/my/image'); done(); @@ -435,34 +436,30 @@ describe('Image', function() { }); }); - describe('#resizeVersions()', function() { - it('resizes image versions', function(done) { - image.getMetadata(image.src, function(err, metadata) { - assert.ifError(err); + describe('#resizeVersions()', () => { + it('resizes image versions', done => { + image.getMetadata(image.src, (err1, metadata) => { + assert.ifError(err1); - image.resizeVersions(function(err, versions) { - assert.ifError(err); + image.resizeVersions((err2, versions) => { + assert.ifError(err2); - var version; - for (var i = 0; i < versions.length; i++) { - version = versions[i]; - require('fs').statSync(version.path); - require('fs').unlinkSync(version.path); - } + versions.forEach(version => { + statSync(version.path); + unlinkSync(version.path); + }); done(); - }, { - metadata: metadata - }); + }, { metadata }); }); }); }); - describe('#uploadVersions()', function() { - it('uploads image versions', function(done) { - var i = 0; + describe('#uploadVersions()', () => { + it('uploads image versions', done => { + let i = 0; - image._upload = function(dest, version, cb) { + image._upload = (dest, version, cb) => { assert.equal(dest, '/foo/bar'); assert.equal(version, i++); cb(null, version + 1); @@ -470,18 +467,18 @@ describe('Image', function() { image.upload.opts.original = undefined; - image.uploadVersions(function(err, versions) { + image.uploadVersions((err, versions) => { assert.ifError(err); assert.deepEqual(versions, [1, 2, 3, 4]); done(); }, { versions: [0, 1, 2, 3], - dest: '/foo/bar' + dest: '/foo/bar', }); }); - it('uploads original image', function(done) { - image._upload = function(dest, version, cb) { + it('uploads original image', done => { + image._upload = (dest, version, cb) => { assert.deepEqual(version, { awsImageAcl: 'public', awsImageExpires: 31536000, @@ -489,7 +486,7 @@ describe('Image', function() { original: true, width: 111, height: 222, - path: image.src + path: image.src, }); cb(null, version); @@ -498,10 +495,10 @@ describe('Image', function() { image.upload.opts.original = { awsImageAcl: 'public', awsImageExpires: 31536000, - awsImageMaxAge: 31536000 + awsImageMaxAge: 31536000, }; - image.uploadVersions(function(err, versions) { + image.uploadVersions((err, versions) => { assert.ifError(err); assert.deepEqual(versions, [{ @@ -511,7 +508,7 @@ describe('Image', function() { original: true, width: 111, height: 222, - path: image.src + path: image.src, }]); done(); @@ -520,41 +517,41 @@ describe('Image', function() { dest: '/foo/bar', metadata: { width: 111, - height: 222 - } + height: 222, + }, }); }); }); - describe('#removeVersions()', function() { - var unlink = require('fs').unlink; - var results = { - uploads: [] + describe('#removeVersions()', () => { + const unlink = fs.unlink; + const results = { + uploads: [], }; - beforeEach(function() { + beforeEach(() => { image.upload.opts.cleanup = {}; results.uploads = [{ original: true, - path: '/foo/bar' + path: '/foo/bar', }, { - path: '/foo/bar-2' + path: '/foo/bar-2', }]; }); - afterEach(function() { - require('fs').unlink = unlink; + afterEach(() => { + fs.unlink = unlink; }); - it('keeps all local images', function(done) { - require('fs').unlink = function() { + it('keeps all local images', done => { + fs.unlink = () => { assert.fail(new Error('unlink shall not be called')); }; image.removeVersions(done, results); }); - it('removes image versions by default', function(done) { - require('fs').unlink = function(path, cb) { + it('removes image versions by default', done => { + fs.unlink = (path, cb) => { assert.equal(path, results.uploads[1].path); cb(); }; @@ -563,8 +560,8 @@ describe('Image', function() { image.removeVersions(done, results); }); - it('removes original image', function(done) { - require('fs').unlink = function(path, cb) { + it('removes original image', done => { + fs.unlink = (path, cb) => { assert.equal(path, results.uploads[0].path); cb(); }; @@ -573,10 +570,10 @@ describe('Image', function() { image.removeVersions(done, results); }); - it('removes all images', function(done) { - var i = 0; + it('removes all images', done => { + let i = 0; - require('fs').unlink = function(path, cb) { + fs.unlink = (path, cb) => { assert.equal(path, results.uploads[i++].path); cb(); }; @@ -588,27 +585,22 @@ describe('Image', function() { }); }); -describe('Integration Tests', function() { - beforeEach(function() { +describe('Integration Tests', () => { + beforeEach(() => { if (process.env.INTEGRATION_TEST !== 'true') { - upload._randomPath = function() { - return '110ec58a-a0f2-4ac4-8393-c866d813b8d1'; - }; + upload._randomPath = () => '110ec58a-a0f2-4ac4-8393-c866d813b8d1'; } }); - it('uploads image to new random path', function(done) { + it('uploads image to new random path', function it(done) { this.timeout(10000); - upload.upload(__dirname + '/assets/portrait.jpg', {}, function(e, images) { + upload.upload(`${__dirname}/assets/portrait.jpg`, {}, (e, images) => { assert.ifError(e); - var image; - for (var i = 0; images.length < 0; i++) { - image = images[i]; - + images.forEach(image => { if (image.key) { cleanup.push({ - Key: image.key + Key: image.key, }); } @@ -625,31 +617,27 @@ describe('Integration Tests', function() { assert.equal(typeof image.height, 'number'); assert.equal(typeof image.width, 'number'); } - } + }); done(); }); }); - it('uploads image to fixed path', function(done) { + it('uploads image to fixed path', function it(done) { this.timeout(10000); - var file = __dirname + '/assets/portrait.jpg'; - var opts = { - path: 'path/to/image' + const file = `${__dirname}/assets/portrait.jpg`; + const opts = { + path: 'path/to/image', }; - upload.upload(file, opts, function(err, images) { + upload.upload(file, opts, (err, images) => { assert.ifError(err); - var image; - - for (var i = 0; i < images.length; i++) { - image = images[i]; - + images.forEach(image => { if (image.key) { cleanup.push({ - Key: image.key + Key: image.key, }); } @@ -666,7 +654,7 @@ describe('Integration Tests', function() { assert.equal(typeof image.height, 'number'); assert.equal(typeof image.width, 'number'); } - } + }); done(); }); diff --git a/test/support/env.js b/test/support/env.js new file mode 100644 index 0000000..55f0aa1 --- /dev/null +++ b/test/support/env.js @@ -0,0 +1,3 @@ +'use strict'; + +process.env.TMP_DIR = '/tmp'; diff --git a/utils/git_hooks/pre-commit b/utils/git_hooks/pre-commit deleted file mode 100755 index aaefeb1..0000000 --- a/utils/git_hooks/pre-commit +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -if git-rev-parse --verify HEAD >/dev/null 2>&1; then - against=HEAD -else - against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 -fi - -for FILE in `git diff-index --name-status --cached $against -- | cut -c3-` ; do - # Check for describe.only(), describe.skip(), it.only(), it.skip() - if egrep -q '(describe|it)\.(only|skip)' "$FILE" - then - echo 'ERROR:' $FILE 'can not have skip() or only() tests' - exit 1 - fi - - # Check for console.log() - if grep -q 'console.log' "$FILE" && [ "$FILE" != 'server.litcoffee' ] - then - echo 'ERROR:' $FILE 'can not use console.log()' - exit 1 - fi - -done -exit - diff --git a/wercker.yml b/wercker.yml index 1d98192..a62ad7b 100644 --- a/wercker.yml +++ b/wercker.yml @@ -8,22 +8,51 @@ build: echo "node version $(node -v) running" echo "npm version $(npm -v) running" - - script: - name: echo imagemagick information - code: echo "$(convert -version)" - - npm-install - script: - name: jshint - code: npm run hint + name: lint + code: npm run lint - npm-test + - script: + name: test coverage + code: | + npm run cover + cat ./coverage/lcov.info | npm run codacy-coverage + + - script: + name: node security project + code: | + npm run nsp + after-steps: - turistforeningen/slack-notifier: url: $SLACK_WEBHOOK_URL -deploy: +npm: steps: + # Rebuild node_modules to fix broken symlinks + # https://github.com/wercker/docs/issues/310 + - script: + name: npm rebuild + code: npm rebuild + + - script: + name: semantic release pre + code: npm run semantic-release -- pre + - turistforeningen/npm-publish + + - script: + name: semantic release post + code: npm run semantic-release -- post + + - script: + name: greenkeeper postpublish + code: npm run greenkeeper-postpublish + + after-steps: + - turistforeningen/slack-notifier: + url: $SLACK_WEBHOOK_URL