Merge branch 'master' of github.com:strongloop/loopback-component-storage
This commit is contained in:
commit
e6e49cf954
|
@ -0,0 +1,23 @@
|
||||||
|
# Number of days of inactivity before an issue becomes stale
|
||||||
|
daysUntilStale: 60
|
||||||
|
# Number of days of inactivity before a stale issue is closed
|
||||||
|
daysUntilClose: 14
|
||||||
|
# Issues with these labels will never be considered stale
|
||||||
|
exemptLabels:
|
||||||
|
- pinned
|
||||||
|
- security
|
||||||
|
- critical
|
||||||
|
- p1
|
||||||
|
- major
|
||||||
|
# Label to use when marking an issue as stale
|
||||||
|
staleLabel: stale
|
||||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
|
closeComment: >
|
||||||
|
This issue has been closed due to continued inactivity. Thank you for your understanding.
|
||||||
|
If you believe this to be in error, please contact one of the code owners,
|
||||||
|
listed in the `CODEOWNERS` file at the top-level of this repository.
|
24
CHANGES.md
24
CHANGES.md
|
@ -1,3 +1,27 @@
|
||||||
|
2017-08-30, Version 3.3.1
|
||||||
|
=========================
|
||||||
|
|
||||||
|
* Declare container parameter for swagger spec (Raymond Feng)
|
||||||
|
|
||||||
|
|
||||||
|
2017-08-30, Version 3.3.0
|
||||||
|
=========================
|
||||||
|
|
||||||
|
* Mark HTTP path parameters as required (Miroslav Bajtoš)
|
||||||
|
|
||||||
|
* Add stalebot configuration (Kevin Delisle)
|
||||||
|
|
||||||
|
* Create Issue and PR Templates (#218) (Sakib Hasan)
|
||||||
|
|
||||||
|
* Update translated strings Q3 2017 (Allen Boone)
|
||||||
|
|
||||||
|
* Add CODEOWNER file (Diana Lau)
|
||||||
|
|
||||||
|
* update messages.json (Diana Lau)
|
||||||
|
|
||||||
|
* add .travis.yml (Diana Lau)
|
||||||
|
|
||||||
|
|
||||||
2017-03-09, Version 3.2.0
|
2017-03-09, Version 3.2.0
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) IBM Corp. 2013,2016. All Rights Reserved.
|
Copyright (c) IBM Corp. 2013,2017. All Rights Reserved.
|
||||||
Node module: loopback-component-storage
|
Node module: loopback-component-storage
|
||||||
This project is licensed under the Artistic License 2.0, full text below.
|
This project is licensed under the Artistic License 2.0, full text below.
|
||||||
|
|
||||||
|
|
13
README.md
13
README.md
|
@ -2,19 +2,20 @@
|
||||||
|
|
||||||
**NOTE: The loopback-component-storage module supersedes [loopback-storage-service](https://www.npmjs.org/package/loopback-storage-service). Please update your package.json accordingly.**
|
**NOTE: The loopback-component-storage module supersedes [loopback-storage-service](https://www.npmjs.org/package/loopback-storage-service). Please update your package.json accordingly.**
|
||||||
|
|
||||||
LoopBack storage component provides Node.js and REST APIs to manage binary contents
|
LoopBack storage component provides Node.js and REST APIs to manage binary file contents
|
||||||
using pluggable storage providers, such as local file systems, Amazon S3, or
|
using pluggable storage providers, such as local file systems, Amazon S3, or
|
||||||
Rackspace cloud files. We use [pkgcloud](https://github.com/pkgcloud/pkgcloud) to support the cloud based
|
Rackspace cloud files. It uses [pkgcloud](https://github.com/pkgcloud/pkgcloud) to support cloud-based
|
||||||
storage services including:
|
storage services including:
|
||||||
|
|
||||||
- Amazon
|
- Amazon
|
||||||
- Rackspace
|
|
||||||
- Openstack
|
|
||||||
- Azure
|
- Azure
|
||||||
|
- Google Cloud
|
||||||
|
- Openstack
|
||||||
|
- Rackspace
|
||||||
|
|
||||||
> Please see the [Storage Service Documentaion](http://loopback.io/doc/en/lb2/Storage-component.html).
|
> Please see the [Storage Service Documentaion](http://loopback.io/doc/en/lb3/Storage-component.html).
|
||||||
|
|
||||||
For more details on the architecture of the module, please see the introduction section of the [blog post](https://strongloop.com/strongblog/managing-nodejs-loopback-storage-service-provider/) written up its launch.
|
For more details on the architecture of the module, please see the introduction section of the [blog post](https://strongloop.com/strongblog/managing-nodejs-loopback-storage-service-provider/).
|
||||||
|
|
||||||
## Use
|
## Use
|
||||||
Now you can use Container's name with slash! If you want to create a directory, like `this/isMy/newContainer`, you have to use the char `%2F` instead of `/`, so your Container's name going to be `this%2FisMy%2FnewContainer`.
|
Now you can use Container's name with slash! If you want to create a directory, like `this/isMy/newContainer`, you have to use the char `%2F` instead of `/`, so your Container's name going to be `this%2FisMy%2FnewContainer`.
|
||||||
|
|
|
@ -166,6 +166,30 @@ exports.upload = function(provider, req, res, options, cb) {
|
||||||
uploadParams.acl = file.acl;
|
uploadParams.acl = file.acl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add AWS specific options
|
||||||
|
// See http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property
|
||||||
|
if (options.StorageClass) {
|
||||||
|
uploadParams.StorageClass = options.StorageClass;
|
||||||
|
}
|
||||||
|
if (options.CacheControl) {
|
||||||
|
uploadParams.CacheControl = options.CacheControl;
|
||||||
|
}
|
||||||
|
if (options.ServerSideEncryption) {
|
||||||
|
uploadParams.ServerSideEncryption = options.ServerSideEncryption;
|
||||||
|
}
|
||||||
|
if (options.SSEKMSKeyId) {
|
||||||
|
uploadParams.SSEKMSKeyId = options.SSEKMSKeyId;
|
||||||
|
}
|
||||||
|
if (options.SSECustomerAlgorithm) {
|
||||||
|
uploadParams.SSECustomerAlgorithm = options.SSECustomerAlgorithm;
|
||||||
|
}
|
||||||
|
if (options.SSECustomerKey) {
|
||||||
|
uploadParams.SSECustomerKey = options.SSECustomerKey;
|
||||||
|
}
|
||||||
|
if (options.SSECustomerKeyMD5) {
|
||||||
|
uploadParams.SSECustomerKeyMD5 = options.SSECustomerKeyMD5;
|
||||||
|
}
|
||||||
|
|
||||||
var writer = provider.upload(uploadParams);
|
var writer = provider.upload(uploadParams);
|
||||||
|
|
||||||
writer.on('error', function(err) {
|
writer.on('error', function(err) {
|
||||||
|
|
|
@ -225,12 +225,21 @@ StorageService.prototype.removeFile = function(container, file, cb) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload middleware for the HTTP request/response <!-- Should this be documented? -->
|
* Upload middleware for the HTTP request/response <!-- Should this be documented? -->
|
||||||
|
* @param {String} [container] Container name
|
||||||
* @param {Request} req Request object
|
* @param {Request} req Request object
|
||||||
* @param {Response} res Response object
|
* @param {Response} res Response object
|
||||||
* @param {Object} [options] Options for upload
|
* @param {Object} [options] Options for upload
|
||||||
* @param {Function} cb Callback function
|
* @param {Function} cb Callback function
|
||||||
*/
|
*/
|
||||||
StorageService.prototype.upload = function(req, res, options, cb) {
|
StorageService.prototype.upload = function(container, req, res, options, cb) {
|
||||||
|
// Test if container is req for backward compatibility
|
||||||
|
if (typeof container === 'object' && container.url && container.method) {
|
||||||
|
// First argument is req, shift all args
|
||||||
|
cb = options;
|
||||||
|
options = res;
|
||||||
|
res = req;
|
||||||
|
req = container;
|
||||||
|
}
|
||||||
if (!cb && 'function' === typeof options) {
|
if (!cb && 'function' === typeof options) {
|
||||||
cb = options;
|
cb = options;
|
||||||
options = {};
|
options = {};
|
||||||
|
@ -253,6 +262,9 @@ StorageService.prototype.upload = function(req, res, options, cb) {
|
||||||
if (this.maxFieldsSize && !options.maxFieldsSize) {
|
if (this.maxFieldsSize && !options.maxFieldsSize) {
|
||||||
options.maxFieldsSize = this.maxFieldsSize;
|
options.maxFieldsSize = this.maxFieldsSize;
|
||||||
}
|
}
|
||||||
|
if (typeof container === 'string') {
|
||||||
|
options.container = container;
|
||||||
|
}
|
||||||
return handler.upload(this.client, req, res, options, cb);
|
return handler.upload(this.client, req, res, options, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -282,7 +294,7 @@ StorageService.prototype.getContainers.http =
|
||||||
|
|
||||||
StorageService.prototype.getContainer.shared = true;
|
StorageService.prototype.getContainer.shared = true;
|
||||||
StorageService.prototype.getContainer.accepts = [
|
StorageService.prototype.getContainer.accepts = [
|
||||||
{arg: 'container', type: 'string'},
|
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
|
||||||
];
|
];
|
||||||
StorageService.prototype.getContainer.returns = {
|
StorageService.prototype.getContainer.returns = {
|
||||||
arg: 'container',
|
arg: 'container',
|
||||||
|
@ -304,7 +316,7 @@ StorageService.prototype.createContainer.http =
|
||||||
|
|
||||||
StorageService.prototype.destroyContainer.shared = true;
|
StorageService.prototype.destroyContainer.shared = true;
|
||||||
StorageService.prototype.destroyContainer.accepts = [
|
StorageService.prototype.destroyContainer.accepts = [
|
||||||
{arg: 'container', type: 'string'},
|
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
|
||||||
];
|
];
|
||||||
StorageService.prototype.destroyContainer.returns = {};
|
StorageService.prototype.destroyContainer.returns = {};
|
||||||
StorageService.prototype.destroyContainer.http =
|
StorageService.prototype.destroyContainer.http =
|
||||||
|
@ -312,7 +324,7 @@ StorageService.prototype.destroyContainer.http =
|
||||||
|
|
||||||
StorageService.prototype.getFiles.shared = true;
|
StorageService.prototype.getFiles.shared = true;
|
||||||
StorageService.prototype.getFiles.accepts = [
|
StorageService.prototype.getFiles.accepts = [
|
||||||
{arg: 'container', type: 'string'},
|
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
|
||||||
];
|
];
|
||||||
StorageService.prototype.getFiles.returns = {arg: 'files', type: 'array', root: true};
|
StorageService.prototype.getFiles.returns = {arg: 'files', type: 'array', root: true};
|
||||||
StorageService.prototype.getFiles.http =
|
StorageService.prototype.getFiles.http =
|
||||||
|
@ -320,8 +332,8 @@ StorageService.prototype.getFiles.http =
|
||||||
|
|
||||||
StorageService.prototype.getFile.shared = true;
|
StorageService.prototype.getFile.shared = true;
|
||||||
StorageService.prototype.getFile.accepts = [
|
StorageService.prototype.getFile.accepts = [
|
||||||
{arg: 'container', type: 'string'},
|
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
|
||||||
{arg: 'file', type: 'string'},
|
{arg: 'file', type: 'string', required: true, 'http': {source: 'path'}},
|
||||||
];
|
];
|
||||||
StorageService.prototype.getFile.returns = {arg: 'file', type: 'object', root: true};
|
StorageService.prototype.getFile.returns = {arg: 'file', type: 'object', root: true};
|
||||||
StorageService.prototype.getFile.http =
|
StorageService.prototype.getFile.http =
|
||||||
|
@ -329,8 +341,8 @@ StorageService.prototype.getFile.http =
|
||||||
|
|
||||||
StorageService.prototype.removeFile.shared = true;
|
StorageService.prototype.removeFile.shared = true;
|
||||||
StorageService.prototype.removeFile.accepts = [
|
StorageService.prototype.removeFile.accepts = [
|
||||||
{arg: 'container', type: 'string'},
|
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
|
||||||
{arg: 'file', type: 'string'},
|
{arg: 'file', type: 'string', required: true, 'http': {source: 'path'}},
|
||||||
];
|
];
|
||||||
StorageService.prototype.removeFile.returns = {};
|
StorageService.prototype.removeFile.returns = {};
|
||||||
StorageService.prototype.removeFile.http =
|
StorageService.prototype.removeFile.http =
|
||||||
|
@ -338,6 +350,7 @@ StorageService.prototype.removeFile.http =
|
||||||
|
|
||||||
StorageService.prototype.upload.shared = true;
|
StorageService.prototype.upload.shared = true;
|
||||||
StorageService.prototype.upload.accepts = [
|
StorageService.prototype.upload.accepts = [
|
||||||
|
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
|
||||||
{arg: 'req', type: 'object', 'http': {source: 'req'}},
|
{arg: 'req', type: 'object', 'http': {source: 'req'}},
|
||||||
{arg: 'res', type: 'object', 'http': {source: 'res'}},
|
{arg: 'res', type: 'object', 'http': {source: 'res'}},
|
||||||
];
|
];
|
||||||
|
@ -347,8 +360,8 @@ StorageService.prototype.upload.http =
|
||||||
|
|
||||||
StorageService.prototype.download.shared = true;
|
StorageService.prototype.download.shared = true;
|
||||||
StorageService.prototype.download.accepts = [
|
StorageService.prototype.download.accepts = [
|
||||||
{arg: 'container', type: 'string', 'http': {source: 'path'}},
|
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
|
||||||
{arg: 'file', type: 'string', 'http': {source: 'path'}},
|
{arg: 'file', type: 'string', required: true, 'http': {source: 'path'}},
|
||||||
{arg: 'req', type: 'object', 'http': {source: 'req'}},
|
{arg: 'req', type: 'object', 'http': {source: 'req'}},
|
||||||
{arg: 'res', type: 'object', 'http': {source: 'res'}},
|
{arg: 'res', type: 'object', 'http': {source: 'res'}},
|
||||||
];
|
];
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
},
|
},
|
||||||
"version": "3.2.0",
|
"version": "3.3.1",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
test.jpg
|
test.jpg
|
||||||
image-*.jpg
|
image-*.jpg
|
||||||
customimagefield_test.jpg
|
customimagefield_test.jpg
|
||||||
|
customimagefield1_test.jpg
|
||||||
|
|
|
@ -32,6 +32,23 @@ app.post('/custom/upload', function(req, res, next) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// custom route with renamer
|
||||||
|
app.post('/custom/uploadWithContainer', function(req, res, next) {
|
||||||
|
var options = {
|
||||||
|
getFilename: function(file, req, res) {
|
||||||
|
return file.field + '_' + file.name;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ds.connector.upload('album1', req, res, options, function(err, result) {
|
||||||
|
if (!err) {
|
||||||
|
res.setHeader('Content-Type', 'application/json');
|
||||||
|
res.status(200).send({result: result});
|
||||||
|
} else {
|
||||||
|
res.status(500).send(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// expose a rest api
|
// expose a rest api
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
|
||||||
|
@ -420,4 +437,20 @@ describe('storage service', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should upload a file with container param', function(done) {
|
||||||
|
request('http://localhost:' + app.get('port'))
|
||||||
|
.post('/custom/uploadWithContainer')
|
||||||
|
.attach('customimagefield1', 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': {'customimagefield1': [
|
||||||
|
{'container': 'album1', 'name': 'customimagefield1_test.jpg',
|
||||||
|
'originalFilename': 'test.jpg', 'type': 'image/jpeg',
|
||||||
|
'field': 'customimagefield1', 'size': 60475},
|
||||||
|
]}, 'fields': {}}});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue