Refactor the storage service

This commit is contained in:
Raymond Feng 2014-01-10 11:34:37 -08:00
parent 890cee40d3
commit 13e9097ff0
15 changed files with 487 additions and 367 deletions

View File

@ -1,7 +1,7 @@
var loopback = require('loopback') var loopback = require('loopback')
, app = module.exports = loopback(); , app = module.exports = loopback();
// var StorageService = require('../'); var path = require('path');
// expose a rest api // expose a rest api
app.use(loopback.rest()); app.use(loopback.rest());
@ -11,31 +11,21 @@ app.configure(function () {
}); });
var ds = loopback.createDataSource({ var ds = loopback.createDataSource({
connector: require('../lib/storage-connector'), connector: require('../index'),
provider: 'filesystem', provider: 'filesystem',
root: '/tmp/storage' root: path.join(__dirname, 'storage')
}); });
var Container = ds.createModel('container', {name: String}); var Container = ds.createModel('container');
console.log(Container);
Container.getContainers(console.log);
console.log('shared', Container.getContainers.shared);
app.model(Container); app.model(Container);
/*
var handler = new StorageService({provider: 'filesystem', root: '/tmp/storage'});
app.service('storage', handler);
app.get('/', function (req, res, next) { app.get('/', function (req, res, next) {
res.setHeader('Content-Type', 'text/html'); res.setHeader('Content-Type', 'text/html');
var form = "<html><body><h1>Storage Service Demo</h1>" + var form = "<html><body><h1>Storage Service Demo</h1>" +
"<a href='/download'>List all containers</a><p>" + "<a href='/containers'>List all containers</a><p>" +
"Upload to container c1: <p>" + "Upload to container c1: <p>" +
"<form method='POST' enctype='multipart/form-data' action='/upload/c1'>" "<form method='POST' enctype='multipart/form-data' action='/containers/container1/upload'>"
+ "File to upload: <input type=file name=uploadedFiles multiple=true><br>" + "File to upload: <input type=file name=uploadedFiles multiple=true><br>"
+ "Notes about the file: <input type=text name=note><br>" + "Notes about the file: <input type=text name=note><br>"
+ "<input type=submit value=Upload></form>" + + "<input type=submit value=Upload></form>" +
@ -44,8 +34,5 @@ app.get('/', function (req, res, next) {
res.end(); res.end();
}); });
*/
app.listen(app.get('port')); app.listen(app.get('port'));
console.log('http://127.0.0.1:' + app.get('port')); console.log('http://127.0.0.1:' + app.get('port'));

6
index.js Normal file
View File

@ -0,0 +1,6 @@
var StorageConnector = require('./lib/storage-connector');
StorageConnector.Container = require('./lib/models/container');
StorageConnector.File = require('./lib/models/file');
module.exports = StorageConnector;

View File

@ -1,150 +0,0 @@
var factory = require('./factory');
var handler = require('./storage-handler');
var storage = require('pkgcloud').storage;
module.exports = StorageService;
/**
* @param options The options to create a provider
* @returns {StorageService}
* @constructor
*/
function StorageService(options) {
if (!(this instanceof StorageService)) {
return new StorageService(options);
}
this.provider = options.provider;
this.client = factory.createClient(options);
}
StorageService.prototype.getContainers = function (cb) {
return this.client.getContainers(cb);
}
StorageService.prototype.createContainer = function (options, cb) {
options = options || {};
if('object' === typeof options && !(options instanceof storage.Container)) {
var Container = factory.getProvider(this.provider).Container;
options = new Container(this.client, options);
}
return this.client.createContainer(options, cb);
}
StorageService.prototype.destroyContainer = function (container, cb) {
return this.client.destroyContainer(container, cb);
}
StorageService.prototype.getContainer = function (container, cb) {
return this.client.getContainer(container, cb);
}
// File related functions
StorageService.prototype.uploadStream = function (container, file, options, cb) {
if(!cb && typeof options === 'function') {
cb = options;
options = {};
}
options = options || {};
if(container) options.container = container;
if(file) options.remote = file;
return this.client.upload(options, cb);
}
StorageService.prototype.downloadStream = function (container, file, options, cb) {
if(!cb && typeof options === 'function') {
cb = options;
options = {};
}
options = options || {};
if(container) options.container = container;
if(file) options.remote = file;
return this.client.download(options, cb);
}
StorageService.prototype.getFiles = function (container, download, cb) {
return this.client.getFiles(container, download, cb);
}
StorageService.prototype.getFile = function (container, file, cb) {
return this.client.getFile(container, file, cb);
}
StorageService.prototype.removeFile = function (container, file, cb) {
return this.client.removeFile(container, file, cb);
}
StorageService.prototype.upload = function (req, res, cb) {
return handler.upload(this.client, req, res, cb);
}
StorageService.prototype.download = function (req, res, cb) {
return handler.download(this.client, req, res, cb);
}
StorageService.modelName = 'storage';
StorageService.prototype.getContainers.shared = true;
StorageService.prototype.getContainers.accepts = [];
StorageService.prototype.getContainers.returns = {arg: 'containers', type: 'array'};
StorageService.prototype.getContainers.http = [
{verb: 'get', path: '/'}
];
StorageService.prototype.getContainer.shared = true;
StorageService.prototype.getContainer.accepts = [{arg: 'container', type: 'string'}];
StorageService.prototype.getContainer.returns = {arg: 'container', type: 'object'};
StorageService.prototype.getContainer.http = [
{verb: 'get', path: '/:container'}
];
StorageService.prototype.createContainer.shared = true;
StorageService.prototype.createContainer.accepts = [{arg: 'options', type: 'object'}];
StorageService.prototype.createContainer.returns = {arg: 'container', type: 'object'};
StorageService.prototype.createContainer.http = [
{verb: 'post', path: '/'}
];
StorageService.prototype.destroyContainer.shared = true;
StorageService.prototype.destroyContainer.accepts = [{arg: 'container', type: 'string'}];
StorageService.prototype.destroyContainer.returns = {};
StorageService.prototype.destroyContainer.http = [
{verb: 'delete', path: '/:container'}
];
StorageService.prototype.getFiles.shared = true;
StorageService.prototype.getFiles.accepts = [{arg: 'container', type: 'string'}];
StorageService.prototype.getFiles.returns = {arg: 'files', type: 'array'};
StorageService.prototype.getFiles.http = [
{verb: 'get', path: '/:container/files'}
];
StorageService.prototype.getFile.shared = true;
StorageService.prototype.getFile.accepts = [{arg: 'container', type: 'string'}, {arg: 'file', type: 'string'}];
StorageService.prototype.getFile.returns = {arg: 'file', type: 'object'};
StorageService.prototype.getFile.http = [
{verb: 'get', path: '/:container/files/:file'}
];
StorageService.prototype.removeFile.shared = true;
StorageService.prototype.removeFile.accepts = [{arg: 'container', type: 'string'}, {arg: 'file', type: 'string'}];
StorageService.prototype.removeFile.returns = {};
StorageService.prototype.removeFile.http = [
{verb: 'delete', path: '/:container/files/:file'}
];
StorageService.prototype.upload.shared = true;
StorageService.prototype.upload.accepts = [{arg: 'req', type: 'undefined', 'http': {source: 'req'}}];
StorageService.prototype.upload.returns = {arg: 'result', type: 'object'};
StorageService.prototype.upload.http = [
{verb: 'post', path: '/:container/upload/:file'}
];
StorageService.prototype.download.shared = true;
StorageService.prototype.download.accepts = [{arg: 'req', type: 'undefined', 'http': {source: 'req'}}];
StorageService.prototype.download.returns = {arg: 'res', type: 'stream'};
StorageService.prototype.download.http = [
{verb: 'get', path: '/:container/download/:file'}
];

8
lib/models/container.js Normal file
View File

@ -0,0 +1,8 @@
var loopback = require('loopback');
var Container = loopback.createModel('container', {
name: String,
url: String
}, {idInjection: false, strict: false});
module.exports = Container;

9
lib/models/file.js Normal file
View File

@ -0,0 +1,9 @@
var loopback = require('loopback');
var File = loopback.createModel('file', {
name: String,
url: String,
container: String
}, {idInjection: false, strict: false});
module.exports = File;

View File

@ -4,15 +4,15 @@ var util = require('util');
exports.Container = Container; exports.Container = Container;
function Container(client, details) { function Container(client, details) {
base.Container.call(this, client, details); base.Container.call(this, client, details);
}; }
util.inherits(Container, base.Container); util.inherits(Container, base.Container);
Container.prototype._setProperties = function(details) { Container.prototype._setProperties = function (details) {
for(var k in details) { for (var k in details) {
if(typeof details[k] !== 'function') { if (typeof details[k] !== 'function') {
this[k] = details[k]; this[k] = details[k];
}
} }
} }
};

View File

@ -4,15 +4,15 @@ var util = require('util');
exports.File = File; exports.File = File;
function File(client, details) { function File(client, details) {
base.File.call(this, client, details); base.File.call(this, client, details);
}; }
util.inherits(File, base.File); util.inherits(File, base.File);
File.prototype._setProperties = function(details) { File.prototype._setProperties = function (details) {
for(var k in details) { for (var k in details) {
if(typeof details[k] !== 'function') { if (typeof details[k] !== 'function') {
this[k] = details[k]; this[k] = details[k];
}
} }
} }
};

View File

@ -3,215 +3,218 @@
*/ */
var fs = require('fs'), var fs = require('fs'),
path = require('path'), path = require('path'),
async = require('async'), async = require('async'),
File = require('./file').File, File = require('./file').File,
Container = require('./container').Container; Container = require('./container').Container;
module.exports.File = File; module.exports.File = File;
module.exports.Container = Container; module.exports.Container = Container;
module.exports.Client = FileSystemProvider; module.exports.Client = FileSystemProvider;
module.exports.createClient = function (options) { module.exports.createClient = function (options) {
return new FileSystemProvider(options); return new FileSystemProvider(options);
}; };
function FileSystemProvider(options) { function FileSystemProvider(options) {
options = options || {}; options = options || {};
this.root = options.root; this.root = options.root;
var exists = fs.existsSync(this.root); var exists = fs.existsSync(this.root);
if (!exists) { if (!exists) {
throw new Error('Path does not exist: ' + this.root); throw new Error('Path does not exist: ' + this.root);
} }
var stat = fs.statSync(this.root); var stat = fs.statSync(this.root);
if (!stat.isDirectory()) { if (!stat.isDirectory()) {
throw new Error('Invalid directory: ' + this.root); throw new Error('Invalid directory: ' + this.root);
} }
} }
var namePattern = new RegExp('[^' + path.sep + '/]+'); var namePattern = new RegExp('[^' + path.sep + '/]+');
function validateName(name, cb) { function validateName(name, cb) {
if (!name) { if (!name) {
cb && process.nextTick(cb.bind(null, new Error('Invalid name: ' + name))); cb && process.nextTick(cb.bind(null, new Error('Invalid name: ' + name)));
if(!cb) { if (!cb) {
console.error('Invalid name: ', name); console.error('Invalid name: ', name);
}
return false;
} }
var match = namePattern.exec(name); return false;
if (match && match.index === 0 && match[0].length === name.length) { }
return true; var match = namePattern.exec(name);
} else { if (match && match.index === 0 && match[0].length === name.length) {
cb && process.nextTick(cb.bind(null, new Error('Invalid name: ' + name))); return true;
if(!cb) { } else {
console.error('Invalid name: ', name); cb && process.nextTick(cb.bind(null, new Error('Invalid name: ' + name)));
} if (!cb) {
return false; console.error('Invalid name: ', name);
} }
return false;
}
} }
// Container related functions // Container related functions
FileSystemProvider.prototype.getContainers = function (cb) { FileSystemProvider.prototype.getContainers = function (cb) {
var self = this; var self = this;
fs.readdir(self.root, function (err, files) { fs.readdir(self.root, function (err, files) {
var containers = []; var containers = [];
var tasks = []; var tasks = [];
files.forEach(function (f) { files.forEach(function (f) {
tasks.push(fs.stat.bind(null, path.join(self.root, f))); tasks.push(fs.stat.bind(null, path.join(self.root, f)));
});
async.parallel(tasks, function (err, stats) {
if (err) {
cb && cb(err);
} else {
stats.forEach(function (stat, index) {
if (stat.isDirectory()) {
var name = files[index];
var props = {name: name};
for (var p in stat) {
props[p] = stat[p];
}
var container = new Container(self, props);
containers.push(container);
}
});
cb && cb(err, containers);
}
});
}); });
} async.parallel(tasks, function (err, stats) {
if (err) {
cb && cb(err);
} else {
stats.forEach(function (stat, index) {
if (stat.isDirectory()) {
var name = files[index];
var props = {name: name};
for (var p in stat) {
props[p] = stat[p];
}
var container = new Container(self, props);
containers.push(container);
}
});
cb && cb(err, containers);
}
});
});
};
FileSystemProvider.prototype.createContainer = function (options, cb) { FileSystemProvider.prototype.createContainer = function (options, cb) {
var self = this; var self = this;
var name = options.name; var name = options.name;
validateName(name, cb) && fs.mkdir(path.join(this.root, name), options, function (err) { validateName(name, cb) && fs.mkdir(path.join(this.root, name), options, function (err) {
cb && cb(err, new Container(self, {name: name})); cb && cb(err, new Container(self, {name: name}));
}); });
} };
FileSystemProvider.prototype.destroyContainer = function (containerName, cb) { FileSystemProvider.prototype.destroyContainer = function (containerName, cb) {
if (!validateName(containerName, cb)) return; if (!validateName(containerName, cb)) return;
var dir = path.join(this.root, containerName); var dir = path.join(this.root, containerName);
fs.readdir(dir, function (err, files) { fs.readdir(dir, function (err, files) {
var tasks = []; var tasks = [];
files.forEach(function (f) { files.forEach(function (f) {
tasks.push(fs.unlink.bind(null, path.join(dir, f))); tasks.push(fs.unlink.bind(null, path.join(dir, f)));
});
async.parallel(tasks, function (err) {
if (err) {
cb && cb(err);
} else {
fs.rmdir(dir, cb);
}
});
}); });
} async.parallel(tasks, function (err) {
if (err) {
cb && cb(err);
} else {
fs.rmdir(dir, cb);
}
});
});
};
FileSystemProvider.prototype.getContainer = function (containerName, cb) { FileSystemProvider.prototype.getContainer = function (containerName, cb) {
var self = this; var self = this;
if (!validateName(containerName, cb)) return; if (!validateName(containerName, cb)) return;
var dir = path.join(this.root, containerName); var dir = path.join(this.root, containerName);
fs.stat(dir, function (err, stat) { fs.stat(dir, function (err, stat) {
var container = null; var container = null;
if (!err) { if (!err) {
var props = {name: containerName}; var props = {name: containerName};
for (var p in stat) { for (var p in stat) {
props[p] = stat[p]; props[p] = stat[p];
} }
container = new Container(self, props); container = new Container(self, props);
} }
cb && cb(err, container); cb && cb(err, container);
}); });
} };
// File related functions // File related functions
FileSystemProvider.prototype.upload = function (options, cb) { FileSystemProvider.prototype.upload = function (options, cb) {
var container = options.container; var container = options.container;
if (!validateName(container, cb)) return; if (!validateName(container, cb)) return;
var file = options.remote; var file = options.remote;
if (!validateName(file, cb)) return; if (!validateName(file, cb)) return;
var filePath = path.join(this.root, container, file); var filePath = path.join(this.root, container, file);
var fileOpts = {flags: 'w+', var fileOpts = {flags: 'w+',
encoding: null, encoding: null,
mode: 0666 }; mode: 0666 };
return fs.createWriteStream(filePath, fileOpts); return fs.createWriteStream(filePath, fileOpts);
} };
FileSystemProvider.prototype.download = function (options, cb) { FileSystemProvider.prototype.download = function (options, cb) {
var container = options.container; var container = options.container;
if (!validateName(container, cb)) return; if (!validateName(container, cb)) return;
var file = options.remote; var file = options.remote;
if (!validateName(file, cb)) return; if (!validateName(file, cb)) return;
var filePath = path.join(this.root, container, file); var filePath = path.join(this.root, container, file);
var fileOpts = {flags: 'r', var fileOpts = {flags: 'r',
autoClose: true }; autoClose: true };
return fs.createReadStream(filePath, fileOpts); return fs.createReadStream(filePath, fileOpts);
};
}
FileSystemProvider.prototype.getFiles = function (container, download, cb) { FileSystemProvider.prototype.getFiles = function (container, download, cb) {
if (typeof download === 'function' && !(download instanceof RegExp)) { if (typeof download === 'function' && !(download instanceof RegExp)) {
cb = download; cb = download;
download = false; download = false;
} }
var self = this; var self = this;
if (!validateName(container, cb)) return; if (!validateName(container, cb)) return;
var dir = path.join(this.root, container); var dir = path.join(this.root, container);
fs.readdir(dir, function (err, entries) { fs.readdir(dir, function (err, entries) {
var files = []; var files = [];
var tasks = []; var tasks = [];
entries.forEach(function (f) { entries.forEach(function (f) {
tasks.push(fs.stat.bind(null, path.join(dir, f))); tasks.push(fs.stat.bind(null, path.join(dir, f)));
});
async.parallel(tasks, function (err, stats) {
if (err) {
cb && cb(err);
} else {
stats.forEach(function (stat, index) {
if (stat.isFile()) {
var props = {container: container, name: entries[index]};
for (var p in stat) {
props[p] = stat[p];
}
var file = new File(self, props);
files.push(file);
}
});
cb && cb(err, files);
}
});
}); });
async.parallel(tasks, function (err, stats) {
} if (err) {
cb && cb(err);
} else {
stats.forEach(function (stat, index) {
if (stat.isFile()) {
var props = {container: container, name: entries[index]};
for (var p in stat) {
props[p] = stat[p];
}
var file = new File(self, props);
files.push(file);
}
});
cb && cb(err, files);
}
});
});
};
FileSystemProvider.prototype.getFile = function (container, file, cb) { FileSystemProvider.prototype.getFile = function (container, file, cb) {
var self = this; var self = this;
if (!validateName(container, cb)) return; if (!validateName(container, cb)) return;
if (!validateName(file, cb)) return; if (!validateName(file, cb)) return;
var filePath = path.join(this.root, container, file); var filePath = path.join(this.root, container, file);
fs.stat(filePath, function (err, stat) { fs.stat(filePath, function (err, stat) {
var f = null; var f = null;
if (!err) { if (!err) {
var props = {container: container, name: file}; var props = {container: container, name: file};
for (var p in stat) { for (var p in stat) {
props[p] = stat[p]; props[p] = stat[p];
} }
f = new File(self, props); f = new File(self, props);
} }
cb && cb(err, f); cb && cb(err, f);
}); });
} };
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(container, cb)) return;
if (!validateName(file, cb)) return; if (!validateName(file, cb)) return;
var filePath = path.join(this.root, container, file); var filePath = path.join(this.root, container, file);
fs.unlink(filePath, cb); fs.unlink(filePath, cb);
} };

View File

@ -1,4 +1,4 @@
var StorageService = require('./index'); var StorageService = require('./storage-service');
/** /**
* Export the initialize method to Loopback data * Export the initialize method to Loopback data
* @param dataSource * @param dataSource
@ -23,4 +23,4 @@ exports.initialize = function (dataSource, callback) {
} }
connector.define = function(model, properties, settings) {}; connector.define = function(model, properties, settings) {};
} };

View File

@ -4,13 +4,14 @@ var StringDecoder = require('string_decoder').StringDecoder;
/** /**
* Handle multipart/form-data upload to the storage service * Handle multipart/form-data upload to the storage service
* @param provider The storage service provider * @param provider The storage service provider
* @param req The HTTP request * @param {Request} req The HTTP request
* @param res The HTTP response * @param {Response} res The HTTP response
* @param cb The callback * @param {String} container The container name
* @param {Function} cb The callback
*/ */
exports.upload = function (provider, req, res, cb) { exports.upload = function (provider, req, res, container, cb) {
var form = new IncomingForm(this.options); var form = new IncomingForm(this.options);
var container = req.params.container; container = container || req.params.container;
var fields = {}, files = {}; var fields = {}, files = {};
form.handlePart = function (part) { form.handlePart = function (part) {
var self = this; var self = this;
@ -104,14 +105,16 @@ exports.upload = function (provider, req, res, cb) {
/** /**
* Handle download from a container/file * Handle download from a container/file
* @param provider The storage service provider * @param provider The storage service provider
* @param req The HTTP request * @param {Request} req The HTTP request
* @param res The HTTP response * @param {Response} res The HTTP response
* @param cb The callback * @param {String} container The container name
* @param {String} file The file name
* @param {Function} cb The callback
*/ */
exports.download = function(provider, req, res, cb) { exports.download = function(provider, req, res, container, file, cb) {
var reader = provider.download({ var reader = provider.download({
container: req.params.container, container: container || req.params.container,
remote: req.params.file remote: file || req.params.file
}); });
reader.pipe(res); reader.pipe(res);
reader.on('error', function(err) { reader.on('error', function(err) {

214
lib/storage-service.js Normal file
View File

@ -0,0 +1,214 @@
var factory = require('./factory');
var handler = require('./storage-handler');
var storage = require('pkgcloud').storage;
var Container = require('./models/container');
var File = require('./models/file');
module.exports = StorageService;
/**
* @param options The options to create a provider
* @returns {StorageService}
* @constructor
*/
function StorageService(options) {
if (!(this instanceof StorageService)) {
return new StorageService(options);
}
this.provider = options.provider;
this.client = factory.createClient(options);
}
function map(obj) {
if(!obj || typeof obj !== 'object') {
return obj;
}
var data = {};
for (var i in obj) {
if (obj.hasOwnProperty(i) && typeof obj[i] !== 'function'
&& typeof obj[i] !== 'object') {
if (i === 'newListener' || i === 'delimiter' || i === 'wildcard') {
// Skip properties from the base class
continue;
}
data[i] = obj[i];
}
}
return data;
}
StorageService.prototype.getContainers = function (cb) {
this.client.getContainers(function(err, containers) {
if(err) {
cb(err, containers);
} else {
cb(err, containers.map(function(c) {
return new Container(map(c));
}));
}
});
};
StorageService.prototype.createContainer = function (options, cb) {
options = options || {};
if ('object' === typeof options && !(options instanceof storage.Container)) {
var Container = factory.getProvider(this.provider).Container;
options = new Container(this.client, options);
}
return this.client.createContainer(options, function(err, container) {
return cb(err, map(container));
});
};
StorageService.prototype.destroyContainer = function (container, cb) {
return this.client.destroyContainer(container, cb);
};
StorageService.prototype.getContainer = function (container, cb) {
return this.client.getContainer(container, function(err, container) {
return cb(err, map(container));
});
};
// File related functions
StorageService.prototype.uploadStream = function (container, file, options, cb) {
if (!cb && typeof options === 'function') {
cb = options;
options = {};
}
options = options || {};
if (container) options.container = container;
if (file) options.remote = file;
return this.client.upload(options, cb);
};
StorageService.prototype.downloadStream = function (container, file, options, cb) {
if (!cb && typeof options === 'function') {
cb = options;
options = {};
}
options = options || {};
if (container) options.container = container;
if (file) options.remote = file;
return this.client.download(options, cb);
};
StorageService.prototype.getFiles = function (container, download, cb) {
return this.client.getFiles(container, download, function(err, files) {
if(err) {
cb(err, files);
} else {
cb(err, files.map(function(f) {
return new File(map(f));
}));
}
});
};
StorageService.prototype.getFile = function (container, file, cb) {
return this.client.getFile(container, file, function(err, f) {
return cb(err, map(f));
});
};
StorageService.prototype.removeFile = function (container, file, cb) {
return this.client.removeFile(container, file, cb);
};
StorageService.prototype.upload = function (req, res, cb) {
return handler.upload(this.client, req, res, req.params.container, cb);
};
StorageService.prototype.download = function (req, res, cb) {
return handler.download(this.client, req, res,
req.params.container, req.params.file, cb);
};
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.http = [
{verb: 'get', path: '/'}
];
StorageService.prototype.getContainer.shared = true;
StorageService.prototype.getContainer.accepts = [
{arg: 'container', type: 'string'}
];
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'}
];
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'}
];
StorageService.prototype.destroyContainer.returns = {};
StorageService.prototype.destroyContainer.http = [
{verb: 'delete', path: '/:container'}
];
StorageService.prototype.getFiles.shared = true;
StorageService.prototype.getFiles.accepts = [
{arg: 'container', type: 'string'}
];
StorageService.prototype.getFiles.returns = {arg: 'files', type: 'array', root: true};
StorageService.prototype.getFiles.http = [
{verb: 'get', path: '/:container/files'}
];
StorageService.prototype.getFile.shared = true;
StorageService.prototype.getFile.accepts = [
{arg: 'container', type: 'string'},
{arg: 'file', type: 'string'}
];
StorageService.prototype.getFile.returns = {arg: 'file', type: 'object', root: true};
StorageService.prototype.getFile.http = [
{verb: 'get', path: '/:container/files/:file'}
];
StorageService.prototype.removeFile.shared = true;
StorageService.prototype.removeFile.accepts = [
{arg: 'container', type: 'string'},
{arg: 'file', type: 'string'}
];
StorageService.prototype.removeFile.returns = {};
StorageService.prototype.removeFile.http = [
{verb: 'delete', path: '/:container/files/:file'}
];
StorageService.prototype.upload.shared = true;
StorageService.prototype.upload.accepts = [
{arg: 'req', type: 'undefined', 'http': {source: 'req'}},
{arg: 'res', type: 'undefined', 'http': {source: 'res'}}
];
StorageService.prototype.upload.returns = {arg: 'result', type: 'object'};
StorageService.prototype.upload.http = [
{verb: 'post', path: '/:container/upload'}
];
StorageService.prototype.download.shared = true;
StorageService.prototype.download.accepts = [
{arg: 'req', type: 'undefined', 'http': {source: 'req'}},
{arg: 'res', type: 'undefined', 'http': {source: 'res'}}
];
StorageService.prototype.download.returns = {arg: 'res', type: 'stream'};
StorageService.prototype.download.http = [
{verb: 'get', path: '/:container/download/:file'}
];

View File

@ -2,12 +2,12 @@
"name": "loopback-storage-service", "name": "loopback-storage-service",
"description": "Loopback Storage Service", "description": "Loopback Storage Service",
"version": "1.0.0", "version": "1.0.0",
"main": "lib/index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "./node_modules/.bin/mocha --timeout 30000 test/*test.js" "test": "./node_modules/.bin/mocha --timeout 30000 test/*test.js"
}, },
"dependencies": { "dependencies": {
"pkgcloud": "~0.8.14", "pkgcloud": "~0.8.17",
"async": "~0.2.9" "async": "~0.2.9"
}, },
"devDependencies": { "devDependencies": {

1
test/images/album1/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
test.jpg

View File

@ -1,4 +1,4 @@
var StorageService = require('../lib/index.js'); var StorageService = require('../lib/storage-service.js');
var assert = require('assert'); var assert = require('assert');
var path = require('path'); var path = require('path');

39
test/upload.test.js Normal file
View File

@ -0,0 +1,39 @@
var request = require('supertest');
var loopback = require('loopback');
var assert = require('assert');
var app = loopback();
var path = require('path');
// expose a rest api
app.use(loopback.rest());
var ds = loopback.createDataSource({
connector: require('../lib/storage-connector'),
provider: 'filesystem',
root: path.join(__dirname, 'images')
});
var Container = ds.createModel('container');
app.model(Container);
describe('storage service', function () {
it('uploads files', function (done) {
var server = app.listen(3000, function () {
request('http://localhost:3000')
.post('/containers/album1/upload')
.attach('image', path.join(__dirname, '../example/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"}
]}, "fields": {}}});
server.close();
done();
});
});
});
});