diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e69de29 diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..c221e63 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,14 @@ +{ + "extends": "loopback", + "rules": { + "max-len": ["error", 90, 4, { + "ignoreComments": true, + "ignoreUrls": true, + "ignorePattern": "^\\s*var\\s.+=\\s*(require\\s*\\()|(/)" + }], + // NOTE we should eventually remove this override + // and fix all of those 100+ violations + "one-var": "off", + "no-unused-expressions": "off" + } +} diff --git a/index.js b/index.js index 64407e2..effcb58 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; var SG = require('strong-globalize'); SG.SetRootDir(__dirname); diff --git a/lib/factory.js b/lib/factory.js index 140ad89..ec61410 100644 --- a/lib/factory.js +++ b/lib/factory.js @@ -2,6 +2,7 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; var pkgcloud = require('pkgcloud'); @@ -13,9 +14,9 @@ function patchBaseClass(cls) { var proto = cls.prototype; var found = false; // Find the prototype that owns the _setProperties method - while (proto - && proto.constructor !== pkgcloud.storage.Container - && proto.constructor !== pkgcloud.storage.File) { + while (proto && + proto.constructor !== pkgcloud.storage.Container && + proto.constructor !== pkgcloud.storage.File) { if (proto.hasOwnProperty('_setProperties')) { found = true; break; @@ -27,13 +28,13 @@ function patchBaseClass(cls) { proto = cls.prototype; } var m1 = proto._setProperties; - proto._setProperties = function (details) { + proto._setProperties = function(details) { // Use an empty object to receive the calculated properties from details var receiver = {}; // Pass in some context as non-enumerable properties Object.defineProperties(receiver, { client: {value: this.client}, - files: {value: this.files} + files: {value: this.files}, }); m1.call(receiver, details); // Apply the calculated properties to this @@ -43,20 +44,19 @@ function patchBaseClass(cls) { // Keep references to raw and the calculated properties this._rawMetadata = details; this._metadata = receiver; // Use _metadata to avoid conflicts - } + }; - proto.toJSON = function () { + proto.toJSON = function() { return this._metadata; }; - proto.getMetadata = function () { + proto.getMetadata = function() { return this._metadata; }; - proto.getRawMetadata = function () { + proto.getRawMetadata = function() { return this._rawMetadata; }; - } /*! * Patch the pkgcloud Container/File classes so that the metadata are separately diff --git a/lib/providers/filesystem/container.js b/lib/providers/filesystem/container.js index fe61d93..326df34 100644 --- a/lib/providers/filesystem/container.js +++ b/lib/providers/filesystem/container.js @@ -2,6 +2,7 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; var base = require('pkgcloud').storage; var util = require('util'); @@ -14,7 +15,7 @@ function Container(client, details) { util.inherits(Container, base.Container); -Container.prototype._setProperties = function (details) { +Container.prototype._setProperties = function(details) { for (var k in details) { if (typeof details[k] !== 'function') { this[k] = details[k]; diff --git a/lib/providers/filesystem/file.js b/lib/providers/filesystem/file.js index f675c55..c6d1b9b 100644 --- a/lib/providers/filesystem/file.js +++ b/lib/providers/filesystem/file.js @@ -2,6 +2,7 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; var base = require('pkgcloud').storage; var util = require('util'); @@ -14,10 +15,10 @@ function File(client, details) { util.inherits(File, base.File); -File.prototype._setProperties = function (details) { +File.prototype._setProperties = function(details) { for (var k in details) { if (typeof details[k] !== 'function') { this[k] = details[k]; } } -}; \ No newline at end of file +}; diff --git a/lib/providers/filesystem/index.js b/lib/providers/filesystem/index.js index 2c4e98c..7a4d9ac 100644 --- a/lib/providers/filesystem/index.js +++ b/lib/providers/filesystem/index.js @@ -3,6 +3,10 @@ // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +// Turning on strict for this file breaks; +// disabling strict for this file +/* eslint-disable strict */ + // Globalization var g = require('strong-globalize')(); @@ -21,7 +25,7 @@ module.exports.storage = module.exports; // To make it consistent with pkgcloud module.exports.File = File; module.exports.Container = Container; module.exports.Client = FileSystemProvider; -module.exports.createClient = function (options) { +module.exports.createClient = function(options) { return new FileSystemProvider(options); }; @@ -79,19 +83,19 @@ function populateMetadata(stat, props) { } } -FileSystemProvider.prototype.getContainers = function (cb) { +FileSystemProvider.prototype.getContainers = function(cb) { var self = this; - fs.readdir(self.root, function (err, files) { + fs.readdir(self.root, function(err, files) { var containers = []; var tasks = []; - files.forEach(function (f) { + files.forEach(function(f) { tasks.push(fs.stat.bind(fs, path.join(self.root, f))); }); - async.parallel(tasks, function (err, stats) { + async.parallel(tasks, function(err, stats) { if (err) { cb && cb(err); } else { - stats.forEach(function (stat, index) { + stats.forEach(function(stat, index) { if (stat.isDirectory()) { var name = files[index]; var props = {name: name}; @@ -106,15 +110,15 @@ FileSystemProvider.prototype.getContainers = function (cb) { }); }; -FileSystemProvider.prototype.createContainer = function (options, cb) { +FileSystemProvider.prototype.createContainer = function(options, cb) { var self = this; var name = options.name; var dir = path.join(this.root, name); - validateName(name, cb) && fs.mkdir(dir, options, function (err) { - if(err) { + validateName(name, cb) && fs.mkdir(dir, options, function(err) { + if (err) { return cb && cb(err); } - fs.stat(dir, function (err, stat) { + fs.stat(dir, function(err, stat) { var container = null; if (!err) { var props = {name: name}; @@ -126,18 +130,18 @@ FileSystemProvider.prototype.createContainer = function (options, cb) { }); }; -FileSystemProvider.prototype.destroyContainer = function (containerName, cb) { +FileSystemProvider.prototype.destroyContainer = function(containerName, cb) { if (!validateName(containerName, cb)) return; var dir = path.join(this.root, containerName); - fs.readdir(dir, function (err, files) { + fs.readdir(dir, function(err, files) { files = files || []; var tasks = []; - files.forEach(function (f) { + files.forEach(function(f) { tasks.push(fs.unlink.bind(fs, path.join(dir, f))); }); - async.parallel(tasks, function (err) { + async.parallel(tasks, function(err) { if (err) { cb && cb(err); } else { @@ -147,11 +151,11 @@ FileSystemProvider.prototype.destroyContainer = function (containerName, cb) { }); }; -FileSystemProvider.prototype.getContainer = function (containerName, cb) { +FileSystemProvider.prototype.getContainer = function(containerName, cb) { var self = this; if (!validateName(containerName, cb)) return; var dir = path.join(this.root, containerName); - fs.stat(dir, function (err, stat) { + fs.stat(dir, function(err, stat) { var container = null; if (!err) { var props = {name: containerName}; @@ -163,7 +167,7 @@ FileSystemProvider.prototype.getContainer = function (containerName, cb) { }; // File related functions -FileSystemProvider.prototype.upload = function (options, cb) { +FileSystemProvider.prototype.upload = function(options, cb) { var container = options.container; if (!validateName(container, cb)) return; var file = options.remote; @@ -172,7 +176,7 @@ FileSystemProvider.prototype.upload = function (options, cb) { var fileOpts = {flags: options.flags || 'w+', encoding: options.encoding || null, - mode: options.mode || 0666 + mode: options.mode || 0666, }; try { @@ -180,7 +184,7 @@ FileSystemProvider.prototype.upload = function (options, cb) { //fixes: https://github.com/strongloop/loopback-component-storage/issues/58 // & #23 & #67 var stream = fs.createWriteStream(filePath, fileOpts); - stream.on('finish', function(){ + stream.on('finish', function() { stream.emit('success'); }); return stream; @@ -189,7 +193,7 @@ FileSystemProvider.prototype.upload = function (options, cb) { } }; -FileSystemProvider.prototype.download = function (options, cb) { +FileSystemProvider.prototype.download = function(options, cb) { var container = options.container; if (!validateName(container, cb)) return; var file = options.remote; @@ -198,11 +202,11 @@ FileSystemProvider.prototype.download = function (options, cb) { var filePath = path.join(this.root, container, file); var fileOpts = {flags: 'r', - autoClose: true }; - + autoClose: true}; + if (options.start) { - fileOpts.start = options.start - fileOpts.end = options.end + fileOpts.start = options.start; + fileOpts.end = options.end; } try { @@ -212,7 +216,7 @@ FileSystemProvider.prototype.download = function (options, cb) { } }; -FileSystemProvider.prototype.getFiles = function (container, options, cb) { +FileSystemProvider.prototype.getFiles = function(container, options, cb) { if (typeof options === 'function' && !(options instanceof RegExp)) { cb = options; options = false; @@ -220,18 +224,18 @@ FileSystemProvider.prototype.getFiles = function (container, options, cb) { var self = this; if (!validateName(container, cb)) return; var dir = path.join(this.root, container); - fs.readdir(dir, function (err, entries) { + fs.readdir(dir, function(err, entries) { entries = entries || []; var files = []; var tasks = []; - entries.forEach(function (f) { + entries.forEach(function(f) { tasks.push(fs.stat.bind(fs, path.join(dir, f))); }); - async.parallel(tasks, function (err, stats) { + async.parallel(tasks, function(err, stats) { if (err) { cb && cb(err); } else { - stats.forEach(function (stat, index) { + stats.forEach(function(stat, index) { if (stat.isFile()) { var props = {container: container, name: entries[index]}; populateMetadata(stat, props); @@ -245,12 +249,12 @@ FileSystemProvider.prototype.getFiles = function (container, options, cb) { }); }; -FileSystemProvider.prototype.getFile = function (container, file, cb) { +FileSystemProvider.prototype.getFile = function(container, file, cb) { var self = this; if (!validateName(container, cb)) return; if (!validateName(file, cb)) return; var filePath = path.join(this.root, container, file); - fs.stat(filePath, function (err, stat) { + fs.stat(filePath, function(err, stat) { var f = null; if (!err) { var props = {container: container, name: file}; @@ -261,13 +265,13 @@ FileSystemProvider.prototype.getFile = function (container, file, cb) { }); }; -FileSystemProvider.prototype.getUrl = function (options) { +FileSystemProvider.prototype.getUrl = function(options) { options = options || {}; var filePath = path.join(this.root, options.container, options.path); return filePath; }; -FileSystemProvider.prototype.removeFile = function (container, file, cb) { +FileSystemProvider.prototype.removeFile = function(container, file, cb) { if (!validateName(container, cb)) return; if (!validateName(file, cb)) return; diff --git a/lib/storage-connector.js b/lib/storage-connector.js index ff0c213..4941637 100644 --- a/lib/storage-connector.js +++ b/lib/storage-connector.js @@ -2,6 +2,7 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; var StorageService = require('./storage-service'); /** @@ -11,14 +12,14 @@ var StorageService = require('./storage-service'); * @callback {Function} callback Callback function * @param {String|Object} err Error string or object */ -exports.initialize = function (dataSource, callback) { +exports.initialize = function(dataSource, callback) { var settings = dataSource.settings || {}; var connector = new StorageService(settings); dataSource.connector = connector; dataSource.connector.dataSource = dataSource; - connector.DataAccessObject = function () { + connector.DataAccessObject = function() { }; for (var m in StorageService.prototype) { var method = StorageService.prototype[m]; @@ -30,6 +31,6 @@ exports.initialize = function (dataSource, callback) { } } - connector.define = function (model, properties, settings) { + connector.define = function(model, properties, settings) { }; }; diff --git a/lib/storage-handler.js b/lib/storage-handler.js index eeba15c..6b2df48 100644 --- a/lib/storage-handler.js +++ b/lib/storage-handler.js @@ -2,6 +2,7 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; // Globalization var g = require('strong-globalize')(); @@ -11,7 +12,7 @@ var StringDecoder = require('string_decoder').StringDecoder; var path = require('path'); var defaultOptions = { - maxFileSize: 10 * 1024 * 1024 // 10 MB + maxFileSize: 10 * 1024 * 1024, // 10 MB }; /** @@ -35,18 +36,22 @@ exports.upload = function(provider, req, res, options, cb) { var form = new IncomingForm(options); var container = options.container || req.params.container; - var fields = {}, files = {}; + var fields = {}; + var files = {}; form.handlePart = function(part) { var self = this; if (part.filename === undefined) { - var value = '' - , decoder = new StringDecoder(this.encoding); + var value = ''; + var decoder = new StringDecoder(this.encoding); part.on('data', function(buffer) { self._fieldsSize += buffer.length; if (self._fieldsSize > self.maxFieldsSize) { - self._error(new Error(g.f('{{maxFieldsSize}} exceeded, received %s bytes of field data', self._fieldsSize))); + self._error(new Error( + g.f('{{maxFieldsSize}} exceeded, received %s bytes of field data', + self._fieldsSize + ))); return; } value += decoder.write(buffer); @@ -70,7 +75,7 @@ exports.upload = function(provider, req, res, options, cb) { var file = { container: container, name: part.filename, - type: part.mime + type: part.mime, }; // Options for this file @@ -91,7 +96,11 @@ exports.upload = function(provider, req, res, options, cb) { } if (Array.isArray(allowedContentTypes) && allowedContentTypes.length !== 0) { if (allowedContentTypes.indexOf(file.type) === -1) { - self._error(new Error(g.f('{{contentType}} "%s" is not allowed (Must be in [%s])', file.type, allowedContentTypes.join(', ')))); + self._error(new Error( + g.f('{{contentType}} "%s" is not allowed (Must be in [%s])', + file.type, + allowedContentTypes.join(', ') + ))); return; } } @@ -121,7 +130,7 @@ exports.upload = function(provider, req, res, options, cb) { var uploadParams = { container: container, remote: file.name, - contentType: file.type + contentType: file.type, }; if (file.acl) { uploadParams.acl = file.acl; @@ -160,14 +169,18 @@ exports.upload = function(provider, req, res, options, cb) { if (fileSize > maxFileSize) { // We are missing some way to tell the provider to cancel upload/multipart upload of the current file. // - s3-upload-stream doesn't provide a way to do this in it's public interface - // - We could call provider.delete file but it would not delete multipart data - self._error(new Error(g.f('{{maxFileSize}} exceeded, received %s bytes of field data (max is %s)', fileSize, maxFileSize))); + // - We could call provider.delete file but it would not delete multipart data + self._error(new Error( + g.f('{{maxFileSize}} exceeded, received %s bytes of field data (max is %s)', + fileSize, + maxFileSize + ))); return; } }); } - part.on("end", function() { + part.on('end', function() { writer.end(); }); part.pipe(writer, {end: false}); @@ -177,6 +190,7 @@ exports.upload = function(provider, req, res, options, cb) { if (err) { console.error(err); } + // eslint-disable-next-line no-unused-expressions cb && cb(err, {files: files, fields: fields}); }); }; @@ -191,7 +205,6 @@ function handleError(res, err) { res.status(500).send({error: err}); } - /** * Handle download from a container/file. * @param {Object} provider The storage service provider @@ -203,42 +216,39 @@ function handleError(res, err) { * @header storageService.download(provider, req, res, container, file, cb) */ exports.download = function(provider, req, res, container, file, cb) { - var fileName = path.basename(file); var params = { container: container || req && req.params.container, - remote: file || req && req.params.file + remote: file || req && req.params.file, }; var range = null; if (req) { - if (req.headers) { range = req.headers.range || ''; } if (range) { provider.getFile(params.container, params.remote, function(err, stats) { - if (err) { handleError(res, err); } else { var total = stats.size; - var parts = range.replace(/bytes=/, "").split("-") - var partialstart = parts[0] - var partialend = parts[1] + var parts = range.replace(/bytes=/, '').split('-'); + var partialstart = parts[0]; + var partialend = parts[1]; - params.start = parseInt(partialstart, 10) - params.end = partialend ? parseInt(partialend, 10) : total - 1 + params.start = parseInt(partialstart, 10); + params.end = partialend ? parseInt(partialend, 10) : total - 1; - var chunksize = (params.end - params.start) + 1 + var chunksize = (params.end - params.start) + 1; - res.status(206) - res.set("Content-Range", "bytes " + params.start + "-" + params.end + "/" + total); - res.set("Accept-Ranges", "bytes"); - res.set("Content-Length", chunksize); + res.status(206); + res.set('Content-Range', 'bytes ' + params.start + '-' + params.end + '/' + total); + res.set('Accept-Ranges', 'bytes'); + res.set('Content-Length', chunksize); var reader = provider.download(params); @@ -253,9 +263,7 @@ exports.download = function(provider, req, res, container, file, cb) { }); } }); - } else { - var reader = provider.download(params); res.type(fileName); @@ -271,6 +279,3 @@ exports.download = function(provider, req, res, container, file, cb) { } }; - - - diff --git a/lib/storage-service.js b/lib/storage-service.js index 6ebf21d..ef50b80 100644 --- a/lib/storage-service.js +++ b/lib/storage-service.js @@ -2,6 +2,7 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; var factory = require('./factory'); var handler = require('./storage-handler'); @@ -45,7 +46,6 @@ function StorageService(options) { if (options.maxFileSize) { this.maxFileSize = options.maxFileSize; } - } function map(obj) { @@ -58,12 +58,12 @@ function map(obj) { * @param {Object|String} err Error string or object * @param {Object[]} containers An array of container metadata objects */ -StorageService.prototype.getContainers = function (cb) { - this.client.getContainers(function (err, containers) { +StorageService.prototype.getContainers = function(cb) { + this.client.getContainers(function(err, containers) { if (err) { cb(err, containers); } else { - cb(err, containers.map(function (c) { + cb(err, containers.map(function(c) { return map(c); })); } @@ -80,14 +80,14 @@ StorageService.prototype.getContainers = function (cb) { * @param {Object} container Container metadata object */ -StorageService.prototype.createContainer = function (options, cb) { +StorageService.prototype.createContainer = function(options, cb) { options = options || {}; if ('object' === typeof options && !(options instanceof storage.Container)) { options.Name = options.name; // Amazon expects Name var Container = factory.getProvider(this.provider).storage.Container; options = new Container(this.client, options); } - return this.client.createContainer(options, function (err, container) { + return this.client.createContainer(options, function(err, container) { return cb(err, map(container)); }); }; @@ -98,7 +98,7 @@ StorageService.prototype.createContainer = function (options, cb) { * @callback {Function} callback Callback function. * @param {Object|String} err Error string or object */ -StorageService.prototype.destroyContainer = function (container, cb) { +StorageService.prototype.destroyContainer = function(container, cb) { return this.client.destroyContainer(container, cb); }; @@ -109,8 +109,8 @@ StorageService.prototype.destroyContainer = function (container, cb) { * @param {Object|String} err Error string or object * @param {Object} container Container metadata object */ -StorageService.prototype.getContainer = function (container, cb) { - return this.client.getContainer(container, function (err, container) { +StorageService.prototype.getContainer = function(container, cb) { + return this.client.getContainer(container, function(err, container) { if (err && err.code === 'ENOENT') { err.statusCode = err.status = 404; return cb(err); @@ -128,7 +128,7 @@ StorageService.prototype.getContainer = function (container, cb) { * @param {String|Object} err Error string or object * @returns {Stream} Stream for uploading */ -StorageService.prototype.uploadStream = function (container, file, options) { +StorageService.prototype.uploadStream = function(container, file, options) { if (typeof options === 'function') { options = {}; } @@ -152,7 +152,7 @@ StorageService.prototype.uploadStream = function (container, file, options) { * @param {String|Object} err Error string or object * @returns {Stream} Stream for downloading */ -StorageService.prototype.downloadStream = function (container, file, options) { +StorageService.prototype.downloadStream = function(container, file, options) { if (typeof options === 'function') { options = {}; } @@ -175,17 +175,17 @@ StorageService.prototype.downloadStream = function (container, file, options) { * @param {Object|String} err Error string or object * @param {Object[]} files An array of file metadata objects */ -StorageService.prototype.getFiles = function (container, options, cb) { - if(typeof options === 'function' && !cb) { +StorageService.prototype.getFiles = function(container, options, cb) { + if (typeof options === 'function' && !cb) { // options argument is not present cb = options; options = {}; } - return this.client.getFiles(container, options, function (err, files) { + return this.client.getFiles(container, options, function(err, files) { if (err) { cb(err, files); } else { - cb(err, files.map(function (f) { + cb(err, files.map(function(f) { return map(f); })); } @@ -200,8 +200,8 @@ StorageService.prototype.getFiles = function (container, options, cb) { * @param {Object|String} err Error string or object * @param {Object} file File metadata object */ -StorageService.prototype.getFile = function (container, file, cb) { - return this.client.getFile(container, file, function (err, f) { +StorageService.prototype.getFile = function(container, file, cb) { + return this.client.getFile(container, file, function(err, f) { return cb(err, map(f)); }); }; @@ -213,7 +213,7 @@ StorageService.prototype.getFile = function (container, file, cb) { * @callback {Function} cb Callback function * @param {Object|String} err Error string or object */ -StorageService.prototype.removeFile = function (container, file, cb) { +StorageService.prototype.removeFile = function(container, file, cb) { return this.client.removeFile(container, file, cb); }; @@ -245,14 +245,14 @@ StorageService.prototype.upload = function(req, res, options, cb) { }; /** - * Download middleware + * Download middleware * @param {String} container Container name * @param {String} file File name * @param {Request} req HTTP request * @param {Response} res HTTP response * @param {Function} cb Callback function */ -StorageService.prototype.download = function (container, file, req, res, cb) { +StorageService.prototype.download = function(container, file, req, res, cb) { return handler.download(this.client, req, res, container, file, cb); }; @@ -260,29 +260,39 @@ StorageService.modelName = 'storage'; StorageService.prototype.getContainers.shared = true; StorageService.prototype.getContainers.accepts = []; -StorageService.prototype.getContainers.returns = {arg: 'containers', type: 'array', root: true}; +StorageService.prototype.getContainers.returns = { + arg: 'containers', + type: 'array', + root: true, +}; StorageService.prototype.getContainers.http = {verb: 'get', path: '/'}; StorageService.prototype.getContainer.shared = true; StorageService.prototype.getContainer.accepts = [ - {arg: 'container', type: 'string'} + {arg: 'container', type: 'string'}, ]; -StorageService.prototype.getContainer.returns = {arg: 'container', type: 'object', root: true}; +StorageService.prototype.getContainer.returns = { + arg: 'container', + type: 'object', root: true, +}; StorageService.prototype.getContainer.http = {verb: 'get', path: '/:container'}; StorageService.prototype.createContainer.shared = true; StorageService.prototype.createContainer.accepts = [ - {arg: 'options', type: 'object', http: {source: 'body'}} + {arg: 'options', type: 'object', http: {source: 'body'}}, ]; -StorageService.prototype.createContainer.returns = {arg: 'container', type: 'object', root: true}; +StorageService.prototype.createContainer.returns = { + arg: 'container', + type: 'object', root: true, +}; StorageService.prototype.createContainer.http = {verb: 'post', path: '/'}; StorageService.prototype.destroyContainer.shared = true; StorageService.prototype.destroyContainer.accepts = [ - {arg: 'container', type: 'string'} + {arg: 'container', type: 'string'}, ]; StorageService.prototype.destroyContainer.returns = {}; StorageService.prototype.destroyContainer.http = @@ -290,7 +300,7 @@ StorageService.prototype.destroyContainer.http = StorageService.prototype.getFiles.shared = true; StorageService.prototype.getFiles.accepts = [ - {arg: 'container', type: 'string'} + {arg: 'container', type: 'string'}, ]; StorageService.prototype.getFiles.returns = {arg: 'files', type: 'array', root: true}; StorageService.prototype.getFiles.http = @@ -299,7 +309,7 @@ StorageService.prototype.getFiles.http = StorageService.prototype.getFile.shared = true; StorageService.prototype.getFile.accepts = [ {arg: 'container', type: 'string'}, - {arg: 'file', type: 'string'} + {arg: 'file', type: 'string'}, ]; StorageService.prototype.getFile.returns = {arg: 'file', type: 'object', root: true}; StorageService.prototype.getFile.http = @@ -308,7 +318,7 @@ StorageService.prototype.getFile.http = StorageService.prototype.removeFile.shared = true; StorageService.prototype.removeFile.accepts = [ {arg: 'container', type: 'string'}, - {arg: 'file', type: 'string'} + {arg: 'file', type: 'string'}, ]; StorageService.prototype.removeFile.returns = {}; StorageService.prototype.removeFile.http = @@ -317,7 +327,7 @@ StorageService.prototype.removeFile.http = StorageService.prototype.upload.shared = true; StorageService.prototype.upload.accepts = [ {arg: 'req', type: 'object', 'http': {source: 'req'}}, - {arg: 'res', type: 'object', 'http': {source: 'res'}} + {arg: 'res', type: 'object', 'http': {source: 'res'}}, ]; StorageService.prototype.upload.returns = {arg: 'result', type: 'object'}; StorageService.prototype.upload.http = @@ -328,7 +338,7 @@ StorageService.prototype.download.accepts = [ {arg: 'container', type: 'string', 'http': {source: 'path'}}, {arg: 'file', type: 'string', 'http': {source: 'path'}}, {arg: 'req', type: 'object', 'http': {source: 'req'}}, - {arg: 'res', type: 'object', 'http': {source: 'res'}} + {arg: 'res', type: 'object', 'http': {source: 'res'}}, ]; StorageService.prototype.download.http = {verb: 'get', path: '/:container/download/:file'}; diff --git a/package.json b/package.json index f8e7547..52dff3e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "version": "1.9.2", "main": "index.js", "scripts": { - "test": "./node_modules/.bin/mocha --timeout 30000 test/*test.js" + "lint": "eslint .", + "test": "./node_modules/.bin/mocha --timeout 30000 test/*test.js", + "posttest": "npm run lint" }, "dependencies": { "async": "^0.9.0", @@ -13,11 +15,13 @@ "strong-globalize": "^2.6.2" }, "devDependencies": { + "eslint": "^2.13.1", + "eslint-config-loopback": "^4.0.0", "express": "^4.11.0", "loopback": "^2.10.0", + "mkdirp": "^0.5.0", "mocha": "^2.1.0", - "supertest": "^0.15.0", - "mkdirp": "^0.5.0" + "supertest": "^0.15.0" }, "repository": { "type": "git", diff --git a/test/fixtures/app.js b/test/fixtures/app.js index 49e4ae6..9090deb 100644 --- a/test/fixtures/app.js +++ b/test/fixtures/app.js @@ -2,9 +2,10 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; -var loopback = require('loopback') - , app = module.exports = loopback(); +var loopback = require('loopback'); +var app = module.exports = loopback(); var path = require('path'); @@ -18,7 +19,7 @@ app.set('port', process.env.PORT || 3000); var ds = loopback.createDataSource({ connector: require('../index'), provider: 'filesystem', - root: path.join(__dirname, 'storage') + root: path.join(__dirname, 'storage'), }); var container = ds.createModel('container'); diff --git a/test/fs.test.js b/test/fs.test.js index e37d841..a9f8c4b 100644 --- a/test/fs.test.js +++ b/test/fs.test.js @@ -2,6 +2,7 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; var FileSystemProvider = require('../lib/providers/filesystem/index.js').Client; @@ -19,16 +20,15 @@ function verifyMetadata(fileOrContainer, name) { assert.equal(typeof fileOrContainer.getMetadata().size, 'number'); } -describe('FileSystem based storage provider', function () { - - describe('container apis', function () { +describe('FileSystem based storage provider', function() { + describe('container apis', function() { var client = null; - it('should require an existing directory as the root', function (done) { + it('should require an existing directory as the root', function(done) { client = new FileSystemProvider({root: path.join(__dirname, 'storage')}); process.nextTick(done); }); - it('should complain if the root directory doesn\'t exist', function (done) { + it('should complain if the root directory doesn\'t exist', function(done) { try { client = new FileSystemProvider({root: path.join(__dirname, '_storage')}); process.nextTick(done.bind(null, 'Error')); @@ -38,98 +38,98 @@ describe('FileSystem based storage provider', function () { } }); - it('should return an empty list of containers', function (done) { - client.getContainers(function (err, containers) { + it('should return an empty list of containers', function(done) { + client.getContainers(function(err, containers) { assert(!err); assert.equal(0, containers.length); done(err, containers); }); }); - it('should create a new container', function (done) { - client.createContainer({name: 'c1'}, function (err, container) { + it('should create a new container', function(done) { + client.createContainer({name: 'c1'}, function(err, container) { assert(!err); verifyMetadata(container, 'c1'); done(err, container); }); }); - it('should get a container c1', function (done) { - client.getContainer('c1', function (err, container) { + it('should get a container c1', function(done) { + client.getContainer('c1', function(err, container) { assert(!err); verifyMetadata(container, 'c1'); done(err, container); }); }); - it('should not get a container c2', function (done) { - client.getContainer('c2', function (err, container) { + it('should not get a container c2', function(done) { + client.getContainer('c2', function(err, container) { assert(err); done(null, container); }); }); - it('should return one container', function (done) { - client.getContainers(function (err, containers) { + it('should return one container', function(done) { + client.getContainers(function(err, containers) { assert(!err); assert.equal(1, containers.length); done(err, containers); }); }); - it('should destroy a container c1', function (done) { - client.destroyContainer('c1', function (err, container) { + it('should destroy a container c1', function(done) { + client.destroyContainer('c1', function(err, container) { assert(!err); done(err, container); }); }); - it('should not get a container c1 after destroy', function (done) { - client.getContainer('c1', function (err, container) { + it('should not get a container c1 after destroy', function(done) { + client.getContainer('c1', function(err, container) { assert(err); done(null, container); }); }); }); - describe('file apis', function () { + describe('file apis', function() { var fs = require('fs'); var client = new FileSystemProvider({root: path.join(__dirname, 'storage')}); - it('should create a new container', function (done) { - client.createContainer({name: 'c1'}, function (err, container) { + it('should create a new container', function(done) { + client.createContainer({name: 'c1'}, function(err, container) { assert(!err); done(err, container); }); }); - it('should upload a file', function (done) { + it('should upload a file', function(done) { var writer = client.upload({container: 'c1', remote: 'f1.txt'}); fs.createReadStream(path.join(__dirname, 'files/f1.txt')).pipe(writer); writer.on('finish', done); writer.on('error', done); }); - it('should download a file', function (done) { + it('should download a file', function(done) { var reader = client.download({ container: 'c1', - remote: 'f1.txt' + remote: 'f1.txt', }); reader.pipe(fs.createWriteStream(path.join(__dirname, 'files/f1_downloaded.txt'))); reader.on('end', done); reader.on('error', done); }); - it('should get files for a container', function (done) { - client.getFiles('c1', function (err, files) { + it('should get files for a container', function(done) { + client.getFiles('c1', function(err, files) { assert(!err); assert.equal(1, files.length); done(err, files); }); }); - it('should get a file', function (done) { - client.getFile('c1', 'f1.txt', function (err, f) { + it('should get a file', function(done) { + client.getFile('c1', 'f1.txt', function(err, f) { assert(!err); assert.ok(f); verifyMetadata(f, 'f1.txt'); @@ -137,30 +137,28 @@ describe('FileSystem based storage provider', function () { }); }); - it('should remove a file', function (done) { - client.removeFile('c1', 'f1.txt', function (err) { + it('should remove a file', function(done) { + client.removeFile('c1', 'f1.txt', function(err) { assert(!err); done(err); }); }); - it('should get no files from a container', function (done) { - client.getFiles('c1', function (err, files) { + it('should get no files from a container', function(done) { + client.getFiles('c1', function(err, files) { assert(!err); assert.equal(0, files.length); done(err, files); }); }); - it('should destroy a container c1', function (done) { - client.destroyContainer('c1', function (err, container) { + it('should destroy a container c1', function(done) { + client.destroyContainer('c1', function(err, container) { // console.error(err); assert(!err); done(err, container); }); }); - }); }); - diff --git a/test/storage-service.test.js b/test/storage-service.test.js index 2037d89..863a54a 100644 --- a/test/storage-service.test.js +++ b/test/storage-service.test.js @@ -2,113 +2,115 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; var StorageService = require('../lib/storage-service.js'); var assert = require('assert'); var path = require('path'); -var storageService = new StorageService({root: path.join(__dirname, 'storage'), provider: 'filesystem'}); +var storageService = new StorageService({ + root: path.join(__dirname, 'storage'), + provider: 'filesystem', +}); -describe('Storage service', function () { - - describe('container apis', function () { - - it('should return an empty list of containers', function (done) { - storageService.getContainers(function (err, containers) { +describe('Storage service', function() { + describe('container apis', function() { + it('should return an empty list of containers', function(done) { + storageService.getContainers(function(err, containers) { assert(!err); assert.equal(0, containers.length); done(err, containers); }); }); - it('should create a new container', function (done) { - storageService.createContainer({name: 'c1'}, function (err, container) { + it('should create a new container', function(done) { + storageService.createContainer({name: 'c1'}, function(err, container) { assert(!err); assert(container.getMetadata()); done(err, container); }); }); - it('should get a container c1', function (done) { - storageService.getContainer('c1', function (err, container) { + it('should get a container c1', function(done) { + storageService.getContainer('c1', function(err, container) { assert(!err); assert(container.getMetadata()); done(err, container); }); }); - it('should not get a container c2', function (done) { - storageService.getContainer('c2', function (err, container) { + it('should not get a container c2', function(done) { + storageService.getContainer('c2', function(err, container) { assert(err); done(null, container); }); }); - it('should return one container', function (done) { - storageService.getContainers(function (err, containers) { + it('should return one container', function(done) { + storageService.getContainers(function(err, containers) { assert(!err); assert.equal(1, containers.length); done(err, containers); }); }); - it('should destroy a container c1', function (done) { - storageService.destroyContainer('c1', function (err, container) { + it('should destroy a container c1', function(done) { + storageService.destroyContainer('c1', function(err, container) { assert(!err); done(err, container); }); }); - it('should not get a container c1 after destroy', function (done) { - storageService.getContainer('c1', function (err, container) { + it('should not get a container c1 after destroy', function(done) { + storageService.getContainer('c1', function(err, container) { assert(err); done(null, container); }); }); }); - describe('file apis', function () { + describe('file apis', function() { var fs = require('fs'); - it('should create a new container', function (done) { - storageService.createContainer({name: 'c1'}, function (err, container) { + it('should create a new container', function(done) { + storageService.createContainer({name: 'c1'}, function(err, container) { assert(!err); done(err, container); }); }); - it('should upload a file', function (done) { + it('should upload a file', function(done) { var writer = storageService.uploadStream('c1', 'f1.txt'); fs.createReadStream(path.join(__dirname, 'files/f1.txt')).pipe(writer); writer.on('finish', done); writer.on('error', done); }); - - it('should emit success event', function (done) { + + it('should emit success event', function(done) { var writer = storageService.uploadStream('c1', 'f1.txt'); fs.createReadStream(path.join(__dirname, 'files/f1.txt')).pipe(writer); writer.on('success', done); writer.on('error', done); }); - it('should download a file', function (done) { + it('should download a file', function(done) { var reader = storageService.downloadStream('c1', 'f1.txt'); reader.pipe(fs.createWriteStream(path.join(__dirname, 'files/f1_downloaded.txt'))); reader.on('end', done); reader.on('error', done); }); - it('should get files for a container', function (done) { - storageService.getFiles('c1', function (err, files) { + it('should get files for a container', function(done) { + storageService.getFiles('c1', function(err, files) { assert(!err); assert.equal(1, files.length); done(err, files); }); }); - it('should get a file', function (done) { - storageService.getFile('c1', 'f1.txt', function (err, f) { + it('should get a file', function(done) { + storageService.getFile('c1', 'f1.txt', function(err, f) { assert(!err); assert.ok(f); assert(f.getMetadata()); @@ -116,30 +118,28 @@ describe('Storage service', function () { }); }); - it('should remove a file', function (done) { - storageService.removeFile('c1', 'f1.txt', function (err) { + it('should remove a file', function(done) { + storageService.removeFile('c1', 'f1.txt', function(err) { assert(!err); done(err); }); }); - it('should get no files from a container', function (done) { - storageService.getFiles('c1', function (err, files) { + it('should get no files from a container', function(done) { + storageService.getFiles('c1', function(err, files) { assert(!err); assert.equal(0, files.length); done(err, files); }); }); - it('should destroy a container c1', function (done) { - storageService.destroyContainer('c1', function (err, container) { + it('should destroy a container c1', function(done) { + storageService.destroyContainer('c1', function(err, container) { // console.error(err); assert(!err); done(err, container); }); }); - }); }); - diff --git a/test/upload-download.test.js b/test/upload-download.test.js index 4b82d5c..883311f 100644 --- a/test/upload-download.test.js +++ b/test/upload-download.test.js @@ -2,6 +2,7 @@ // Node module: loopback-component-storage // This file is licensed under the Artistic License 2.0. // License text available at https://opensource.org/licenses/Artistic-2.0 +'use strict'; var request = require('supertest'); var loopback = require('loopback'); @@ -23,7 +24,7 @@ var dsImage = loopback.createDataSource({ }, acl: 'public-read', allowedContentTypes: ['image/png', 'image/jpeg'], - maxFileSize: 5 * 1024 * 1024 + maxFileSize: 5 * 1024 * 1024, }); var ImageContainer = dsImage.createModel('imageContainer'); @@ -32,7 +33,7 @@ app.model(ImageContainer); var ds = loopback.createDataSource({ connector: require('../lib/storage-connector'), provider: 'filesystem', - root: path.join(__dirname, 'images') + root: path.join(__dirname, 'images'), }); var Container = ds.createModel('container', {}, {base: 'Model'}); @@ -66,51 +67,48 @@ function verifyMetadata(containerOrFile, name) { assert.equal(typeof containerOrFile.size, 'number'); } -describe('storage service', function () { +describe('storage service', function() { var server = null; - before(function (done) { - server = app.listen(0, function () { + before(function(done) { + server = app.listen(0, function() { done(); }); }); - after(function () { + after(function() { server.close(); }); - it('should create a container', function (done) { - + it('should create a container', function(done) { request('http://localhost:' + app.get('port')) .post('/containers') .send({name: 'test-container'}) .set('Accept', 'application/json') .set('Content-Type', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { + .expect(200, function(err, res) { verifyMetadata(res.body, 'test-container'); done(); }); }); - it('should get a container', function (done) { - + it('should get a container', function(done) { request('http://localhost:' + app.get('port')) .get('/containers/test-container') .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { + .expect(200, function(err, res) { verifyMetadata(res.body, 'test-container'); done(); }); }); - it('should list containers', function (done) { - + it('should list containers', function(done) { request('http://localhost:' + app.get('port')) .get('/containers') .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { + .expect(200, function(err, res) { assert(Array.isArray(res.body)); assert.equal(res.body.length, 2); res.body.forEach(function(c) { @@ -120,37 +118,34 @@ describe('storage service', function () { }); }); - it('should delete a container', function (done) { - + it('should delete a container', function(done) { request('http://localhost:' + app.get('port')) .del('/containers/test-container') .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { + .expect(200, function(err, res) { done(); }); }); - it('should list containers after delete', function (done) { - + it('should list containers after delete', function(done) { request('http://localhost:' + app.get('port')) .get('/containers') .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { + .expect(200, function(err, res) { assert(Array.isArray(res.body)); assert.equal(res.body.length, 1); done(); }); }); - it('should list files', function (done) { - + it('should list files', function(done) { request('http://localhost:' + app.get('port')) .get('/containers/album1/files') .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { + .expect(200, function(err, res) { assert(Array.isArray(res.body)); res.body.forEach(function(f) { verifyMetadata(f); @@ -159,33 +154,31 @@ describe('storage service', function () { }); }); - it('uploads files', function (done) { - + it('uploads files', function(done) { request('http://localhost:' + app.get('port')) .post('/containers/album1/upload') .attach('image', path.join(__dirname, './fixtures/test.jpg')) .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { - assert.deepEqual(res.body, {"result": {"files": {"image": [ - {"container": "album1", "name": "test.jpg", "type": "image/jpeg", - "size": 60475} - ]}, "fields": {}}}); + .expect(200, function(err, res) { + assert.deepEqual(res.body, {'result': {'files': {'image': [ + {'container': 'album1', 'name': 'test.jpg', 'type': 'image/jpeg', + 'size': 60475}, + ]}, 'fields': {}}}); done(); }); }); - it('uploads files with renamer', function (done) { - + it('uploads files with renamer', function(done) { request('http://localhost:' + app.get('port')) .post('/imageContainers/album1/upload') .attach('image', path.join(__dirname, './fixtures/test.jpg')) .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { - assert.deepEqual(res.body, {"result": {"files": {"image": [ - {"container": "album1", "name": "image-test.jpg", "originalFilename":"test.jpg", "type": "image/jpeg", "acl":"public-read", "size": 60475} - ]}, "fields": {}}}); + .expect(200, function(err, res) { + assert.deepEqual(res.body, {'result': {'files': {'image': [ + {'container': 'album1', 'name': 'image-test.jpg', 'originalFilename': 'test.jpg', 'type': 'image/jpeg', 'acl': 'public-read', 'size': 60475}, + ]}, 'fields': {}}}); done(); }); }); @@ -203,51 +196,47 @@ describe('storage service', function () { }); }); - it('uploads file too large', function (done) { - + it('uploads file too large', function(done) { request('http://localhost:' + app.get('port')) .post('/imageContainers/album1/upload') .attach('image', path.join(__dirname, './fixtures/largeImage.jpg')) .set('Accept', 'application/json') .set('Connection', 'keep-alive') .expect('Content-Type', /json/) - .expect(200, function (err, res) { + .expect(200, function(err, res) { assert(err); assert(res.body.error.message.indexOf('maxFileSize exceeded') !== -1); done(); }); }); - it('should get file by name', function (done) { - + it('should get file by name', function(done) { request('http://localhost:' + app.get('port')) .get('/containers/album1/files/test.jpg') .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { + .expect(200, function(err, res) { verifyMetadata(res.body, 'test.jpg'); done(); }); }); - it('should get file by renamed file name', function (done) { - + it('should get file by renamed file name', function(done) { request('http://localhost:' + app.get('port')) .get('/imageContainers/album1/files/image-test.jpg') .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { + .expect(200, function(err, res) { verifyMetadata(res.body, 'image-test.jpg'); done(); }); }); - it('downloads files', function (done) { - + it('downloads files', function(done) { request('http://localhost:' + app.get('port')) .get('/containers/album1/download/test.jpg') .expect('Content-Type', 'image/jpeg') - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); done(); }); @@ -293,23 +282,21 @@ describe('storage service', function () { }); }); - it('should delete a file', function (done) { - + it('should delete a file', function(done) { request('http://localhost:' + app.get('port')) .del('/containers/album1/files/test.jpg') .set('Accept', 'application/json') .expect('Content-Type', /json/) - .expect(200, function (err, res) { + .expect(200, function(err, res) { done(); }); }); - it('reports errors if it fails to find the file to download', function (done) { - + it('reports errors if it fails to find the file to download', function(done) { request('http://localhost:' + app.get('port')) .get('/containers/album1/download/test_not_exist.jpg') .expect('Content-Type', /json/) - .expect(500, function (err, res) { + .expect(500, function(err, res) { assert(res.body.error); done(); });