Compare commits

..

No commits in common. "master" and "v3.3.0" have entirely different histories.

30 changed files with 166 additions and 887 deletions

37
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,37 @@
<!--
Questions:
https://groups.google.com/forum/#!forum/loopbackjs
https://gitter.im/strongloop/loopback
Immediate support:
https://strongloop.com/api-connect-faqs/
https://strongloop.com/node-js/subscription-plans/
-->
# Description/Steps to reproduce
<!--
If feature: A description of the feature
If bug: Steps to reproduce
-->
# Link to reproduction sandbox
<!--
Link to an app sandbox for reproduction
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
-->
# Expected result
<!--
Also include actual results if bug
-->
# Additional information
<!--
Copy+paste the output of these two commands:
node -e 'console.log(process.platform, process.arch, process.versions.node)'
npm ls --prod --depth 0 | grep loopback
-->

View File

@ -1,50 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
labels: bug
---
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
HELP US HELP YOU, PLEASE
- Do a quick search to avoid duplicate issues
- Provide as much information as possible (reproduction sandbox, use case for features, etc.)
- Consider using a more suitable venue for questions such as Stack Overflow, Gitter, etc.
Please fill in the *entire* template below.
-->
## Steps to reproduce
<!-- Describe how to reproduce the issue -->
## Current Behavior
<!-- Describe the observed result -->
## Expected Behavior
<!-- Describe what did you expect instead, what is the desired outcome? -->
## Link to reproduction sandbox
<!--
See https://loopback.io/doc/en/contrib/Reporting-issues.html#loopback-3x-bugs
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
-->
## Additional information
<!--
Copy+paste the output of these two commands:
node -e 'console.log(process.platform, process.arch, process.versions.node)'
npm ls --prod --depth 0 | grep loopback
-->
## Related Issues
<!-- Did you find other bugs that looked similar? -->
_See [Reporting Issues](http://loopback.io/doc/en/contrib/Reporting-issues.html) for more tips on writing good issues_

View File

@ -1,25 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
labels: feature
---
## Suggestion
<!-- A summary of what you'd like to see added or changed -->
## Use Cases
<!--
What do you want to use this for?
What shortcomings exist with current approaches?
-->
## Examples
<!-- Show how this would be used and what the behavior would be -->
## Acceptance criteria
TBD - will be filled by the team.

View File

@ -1,27 +0,0 @@
---
name: Question
about: The issue tracker is not for questions. Please use Stack Overflow or other resources for help.
labels: question
---
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
THE ISSUE TRACKER IS NOT FOR QUESTIONS.
DO NOT CREATE A NEW ISSUE TO ASK A QUESTION.
Please use one of the following resources for help:
**Questions**
- https://stackoverflow.com/tags/loopbackjs
- https://groups.google.com/forum/#!forum/loopbackjs
- https://gitter.im/strongloop/loopback
**Immediate support**
- https://strongloop.com/api-connect-faqs/
- https://strongloop.com/node-js/subscription-plans/
-->

View File

@ -1,11 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Report a security vulnerability
url: https://loopback.io/doc/en/contrib/Reporting-issues.html#security-issues
about: Do not report security vulnerabilities using GitHub issues. Please send an email to `reachsl@us.ibm.com` instead.
- name: Get help on StackOverflow
url: https://stackoverflow.com/tags/loopbackjs
about: Please ask and answer questions on StackOverflow.
- name: Join our mailing list
url: https://groups.google.com/forum/#!forum/loopbackjs
about: You can also post your question to our mailing list.

View File

@ -1,18 +1,25 @@
### Description
#### Related issues
<!--
Please provide a high-level description of the changes made by your pull request.
Please use the following link syntaxes:
Include references to all related GitHub issues and other pull requests, for example:
Fixes #123
Implements #254
See also #23
- connect to #49 (to reference issues in the current repository)
- connect to strongloop/loopback#49 (to reference issues in another repository)
-->
## Checklist
- connect to <link_to_referenced_issue>
👉 [Read and sign the CLA (Contributor License Agreement)](https://cla.strongloop.com/agreements/strongloop/loopback-component-storage) 👈
### Checklist
<!--
- Please mark your choice with an "x" (i.e. [x], see
https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments)
- PR's without test coverage will be closed.
-->
- [ ] `npm test` passes on your machine
- [ ] New tests added or existing tests modified to cover all changes
- [ ] Code conforms with the [style guide](https://loopback.io/doc/en/contrib/style-guide-es6.html)
- [ ] Commit messages are following our [guidelines](https://loopback.io/doc/en/contrib/git-commit-messages.html)
- [ ] Code conforms with the [style
guide](http://loopback.io/doc/en/contrib/style-guide.html)

1
.npmrc
View File

@ -1 +0,0 @@
package-lock=false

View File

@ -1,5 +1,5 @@
language: node_js
node_js:
- "4"
- "6"
- "8"
- "10"
- "12"

View File

@ -1,81 +1,3 @@
2020-03-06, Version 3.7.0
=========================
* Update LTS status in README (Miroslav Bajtoš)
* chore: update copyright year (Diana Lau)
* Update README.md (Shaun)
* chore: improve issue and PR templates (Nora)
* chore: drop Node.js 6 and add Node.js 12 to travis (Nora)
2019-07-25, Version 3.6.3
=========================
* Rannig from other paths. Property files to array. (Diego A. Zapata Häntsch)
2019-07-12, Version 3.6.2
=========================
* chore: update CODEOWNERS (Diana Lau)
* Upgrade pkgcloud version (Diego A. Zapata Häntsch)
2019-05-23, Version 3.6.1
=========================
* update pkgcloud and use version 2.x (Anis)
* chore: update copyrights years (Agnes Lin)
2019-03-28, Version 3.6.0
=========================
* Pass through AWS/S3 specific options (Alex Owen)
* add support to promise (Matteo Padovano)
* style: fix linting (virkt25)
* {download,upload}Stream: removed callback from doc (Youcef Mammar)
2018-07-09, Version 3.5.0
=========================
* [WebFM] cs/pl/ru translation (candytangnb)
2018-06-20, Version 3.4.0
=========================
* added missing tests (Cory Gottschalk)
* Updated "getFile" to send a 404 for ENOENT errors (Cory Gottschalk)
* chore: update node support and versions (Taranveer Virk)
* Add some debug strings to handler and service (Remi Beges)
* chore: update license (Diana Lau)
* add AWS S3 options for server side encryption (Timo Wolf)
* Update README.md (Rand McKinney)
2017-08-30, Version 3.3.1
=========================
* Declare container parameter for swagger spec (Raymond Feng)
2017-08-30, Version 3.3.0
=========================

View File

@ -1,9 +1,6 @@
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners,
# the last matching pattern has the most precedence.
# Alumni members
# @kjdelisle @loay @ssh24 @virkt25
# the last matching pattern has the most precendence.
# Core team members from IBM
* @jannyHou @b-admike @dhmlau @hacksparrow
* @kjdelisle @jannyHou @loay @b-admike @ssh24 @virkt25 @dhmlau

View File

@ -1,4 +1,4 @@
Copyright (c) IBM Corp. 2013,2017. All Rights Reserved.
Copyright (c) IBM Corp. 2013,2016. All Rights Reserved.
Node module: loopback-component-storage
This project is licensed under the Artistic License 2.0, full text below.

View File

@ -1,43 +1,21 @@
# LoopBack Storage Component
**⚠️ LoopBack 3 is in Maintenance LTS mode, only critical bugs and critical
security fixes will be provided. (See
[Module Long Term Support Policy](#module-long-term-support-policy) below.)**
**NOTE: The loopback-component-storage module supersedes [loopback-storage-service](https://www.npmjs.org/package/loopback-storage-service). Please update your package.json accordingly.**
We urge all LoopBack 3 users to migrate their applications to LoopBack 4 as
soon as possible. Refer to our
[Migration Guide](https://loopback.io/doc/en/lb4/migration-overview.html)
for more information on how to upgrade.
## Overview
LoopBack storage component provides Node.js and REST APIs to manage binary file contents
LoopBack storage component provides Node.js and REST APIs to manage binary contents
using pluggable storage providers, such as local file systems, Amazon S3, or
Rackspace cloud files. It uses [pkgcloud](https://github.com/pkgcloud/pkgcloud) to support cloud-based
Rackspace cloud files. We use [pkgcloud](https://github.com/pkgcloud/pkgcloud) to support the cloud based
storage services including:
- Amazon
- Azure
- Google Cloud
- Openstack
- Rackspace
- Openstack
- Azure
> Please see the [Storage Service Documentation](http://loopback.io/doc/en/lb3/Storage-component.html).
> Please see the [Storage Service Documentaion](http://loopback.io/doc/en/lb2/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/).
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.
## Examples
See https://github.com/strongloop/loopback-example-storage.
## Module Long Term Support Policy
This module adopts the [
Module Long Term Support (LTS)](http://github.com/CloudNativeJS/ModuleLTS) policy,
with the following End Of Life (EOL) dates:
| Version | Status | Published | EOL |
| ------- | --------------- | --------- | -------- |
| 3.x | Maintenance LTS | Dec 2016 | Dec 2020 |
Learn more about our LTS plan in [docs](https://loopback.io/doc/en/contrib/Long-term-support.html).

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// 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');

View File

@ -1,10 +0,0 @@
{
"2eb418c4dc7f7a3e989bb71a8f5388d7": "{{FileSystemProvider}}: Cesta neexistuje: {0}",
"45c1c136e750c62179d75a1c99151281": "{{maxFileSize}} překročena, přijato {0} bajtů dat pole (maximum je {1})",
"6af59b6408b92f4c6b13a2c9b06379f2": "{{FileSystemProvider}}: Neplatný název: {0}",
"78f6f36e8300e15cff778496fb1dd178": "{{contentType}} \"{0}\" není povolen (musí být v [{1}])",
"b8a9e184534171cf66caf58d29ad76f5": "{{maxFieldsSize}} překročena, přijato {0} bajtů dat pole",
"c9fb0aba850059a14f4ed5e045e4ec3e": "Neplatný název: {0}",
"f589fe721f4e6fa112d1f66081ed29ac": "{{FileSystemProvider}}: Neplatný adresář: {0}"
}

View File

@ -1,10 +0,0 @@
{
"2eb418c4dc7f7a3e989bb71a8f5388d7": "{{FileSystemProvider}}: Ścieżka nie istnieje: {0}",
"45c1c136e750c62179d75a1c99151281": "Przekroczono {{maxFileSize}}, odebrano {0} B danych pola (maksymalnie {1})",
"6af59b6408b92f4c6b13a2c9b06379f2": "{{FileSystemProvider}}: niepoprawna nazwa: {0}",
"78f6f36e8300e15cff778496fb1dd178": "Element {{contentType}} \"{0}\" jest niedozwolony (musi być w [{1}])",
"b8a9e184534171cf66caf58d29ad76f5": "Przekroczono {{maxFieldsSize}}, odebrano {0} B danych pola",
"c9fb0aba850059a14f4ed5e045e4ec3e": "Niepoprawna nazwa: {0}",
"f589fe721f4e6fa112d1f66081ed29ac": "{{FileSystemProvider}}: niepoprawny katalog: {0}"
}

View File

@ -1,10 +0,0 @@
{
"2eb418c4dc7f7a3e989bb71a8f5388d7": "{{FileSystemProvider}}: путь не существует: {0}",
"45c1c136e750c62179d75a1c99151281": "Превышен {{maxFileSize}}, получено {0} байт данных поля (максимальное значение - {1})",
"6af59b6408b92f4c6b13a2c9b06379f2": "{{FileSystemProvider}}: недопустимое имя: {0}",
"78f6f36e8300e15cff778496fb1dd178": "{{contentType}} \"{0}\" не разрешен (должен быть в [{1}])",
"b8a9e184534171cf66caf58d29ad76f5": "Превышено значение {{maxFieldsSize}}, получено {0} байт данных поля",
"c9fb0aba850059a14f4ed5e045e4ec3e": "Недопустимое имя: {0}",
"f589fe721f4e6fa112d1f66081ed29ac": "{{FileSystemProvider}}: недопустимый каталог: {0}"
}

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
// Copyright IBM Corp. 2013,2014. All Rights Reserved.
// 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');

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
// Copyright IBM Corp. 2013,2014. All Rights Reserved.
// 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;

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
// Copyright IBM Corp. 2013,2014. All Rights Reserved.
// 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;

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
// Copyright IBM Corp. 2013,2015. All Rights Reserved.
// 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
@ -19,8 +18,6 @@ var fs = require('fs'),
File = require('./file').File,
Container = require('./container').Container;
var utils = require('./../../utils');
module.exports.storage = module.exports; // To make it consistent with pkgcloud
module.exports.File = File;
@ -32,12 +29,6 @@ module.exports.createClient = function(options) {
function FileSystemProvider(options) {
options = options || {};
if (!path.isAbsolute(options.root)) {
var basePath = path.dirname(path.dirname(require.main.filename));
options.root = path.join(basePath, options.root);
}
this.root = options.root;
var exists = fs.existsSync(this.root);
if (!exists) {
@ -104,17 +95,10 @@ function populateMetadata(stat, props) {
}
FileSystemProvider.prototype.getContainers = function(cb) {
cb = cb || utils.createPromiseCallback();
var self = this;
fs.readdir(self.root, function(err, files) {
var containers = [];
var tasks = [];
if (!files) {
files = [];
}
files.forEach(function(f) {
tasks.push(fs.stat.bind(fs, path.join(self.root, f)));
});
@ -135,20 +119,15 @@ FileSystemProvider.prototype.getContainers = function(cb) {
}
});
});
return cb.promise;
};
FileSystemProvider.prototype.createContainer = function(options, cb) {
cb = cb || utils.createPromiseCallback();
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) {
cb && cb(err);
return;
return cb && cb(err);
}
fs.stat(dir, function(err, stat) {
var container = null;
@ -160,13 +139,9 @@ FileSystemProvider.prototype.createContainer = function(options, cb) {
cb && cb(err, container);
});
});
return cb.promise;
};
FileSystemProvider.prototype.destroyContainer = function(containerName, cb) {
cb = cb || utils.createPromiseCallback();
if (!validateName(containerName, cb)) return;
var dir = path.join(this.root, containerName);
@ -185,13 +160,9 @@ FileSystemProvider.prototype.destroyContainer = function(containerName, cb) {
}
});
});
return cb.promise;
};
FileSystemProvider.prototype.getContainer = function(containerName, cb) {
cb = cb || utils.createPromiseCallback();
var self = this;
if (!validateName(containerName, cb)) return;
var dir = path.join(this.root, containerName);
@ -204,8 +175,6 @@ FileSystemProvider.prototype.getContainer = function(containerName, cb) {
}
cb && cb(err, container);
});
return cb.promise;
};
// File related functions
@ -283,9 +252,6 @@ FileSystemProvider.prototype.getFiles = function(container, options, cb) {
cb = options;
options = false;
}
cb = cb || utils.createPromiseCallback();
var self = this;
if (!validateName(container, cb)) return;
var dir = path.join(this.root, container);
@ -312,13 +278,9 @@ FileSystemProvider.prototype.getFiles = function(container, options, cb) {
}
});
});
return cb.promise;
};
FileSystemProvider.prototype.getFile = function(container, file, cb) {
cb = cb || utils.createPromiseCallback();
var self = this;
if (!validateName(container, cb)) return;
if (!validateName(file, cb)) return;
@ -332,8 +294,6 @@ FileSystemProvider.prototype.getFile = function(container, file, cb) {
}
cb && cb(err, f);
});
return cb.promise;
};
FileSystemProvider.prototype.getUrl = function(options) {
@ -343,13 +303,9 @@ FileSystemProvider.prototype.getUrl = function(options) {
};
FileSystemProvider.prototype.removeFile = function(container, file, cb) {
cb = cb || utils.createPromiseCallback();
if (!validateName(container, cb)) return;
if (!validateName(file, cb)) return;
var filePath = path.join(this.root, container, file);
fs.unlink(filePath, cb);
return cb.promise;
};

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
// Copyright IBM Corp. 2013,2014. All Rights Reserved.
// 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');

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
// Copyright IBM Corp. 2013,2015. All Rights Reserved.
// 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
@ -12,7 +11,6 @@ var IncomingForm = require('formidable');
var StringDecoder = require('string_decoder').StringDecoder;
var path = require('path');
var uuid = require('uuid');
var debug = require('debug')('loopback:storage:handler');
var defaultOptions = {
maxFileSize: 10 * 1024 * 1024, // 10 MB
@ -39,7 +37,6 @@ exports.upload = function(provider, req, res, options, cb) {
var form = new IncomingForm(options);
var container = options.container || req.params.container;
debug('Uploading to container with options %o', options);
var fields = {};
var files = {};
form.handlePart = function(part) {
@ -53,11 +50,9 @@ exports.upload = function(provider, req, res, options, cb) {
self._fieldsSize += buffer.length;
if (self._fieldsSize > self.maxFieldsSize) {
self._error(new Error(
g.f(
'{{maxFieldsSize}} exceeded, received %s bytes of field data',
g.f('{{maxFieldsSize}} exceeded, received %s bytes of field data',
self._fieldsSize
)
));
)));
return;
}
value += decoder.write(buffer);
@ -107,12 +102,10 @@ 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])',
g.f('{{contentType}} "%s" is not allowed (Must be in [%s])',
file.type,
allowedContentTypes.join(', ')
)
));
)));
return;
}
}
@ -148,23 +141,6 @@ exports.upload = function(provider, req, res, options, cb) {
uploadParams.acl = file.acl;
}
// AWS specific options
// See http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property
const awsOptionNames = [
'StorageClass',
'CacheControl',
'ServerSideEncryption',
'SSEKMSKeyId',
'SSECustomerAlgorithm',
'SSECustomerKey',
'SSECustomerKeyMD5',
];
for (const awsOption of awsOptionNames) {
if (typeof options[awsOption] !== 'undefined') {
uploadParams[awsOption] = options[awsOption];
}
}
var writer = provider.upload(uploadParams);
writer.on('error', function(err) {
@ -201,12 +177,10 @@ exports.upload = function(provider, req, res, options, cb) {
// - 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)',
g.f('{{maxFileSize}} exceeded, received %s bytes of field data (max is %s)',
fileSize,
maxFileSize
)
));
)));
return;
}
});

View File

@ -1,16 +1,13 @@
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// 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');
var utils = require('./utils');
var storage = require('pkgcloud').storage;
var debug = require('debug')('loopback:storage:service');
module.exports = StorageService;
@ -55,22 +52,6 @@ function StorageService(options) {
if (options.maxFieldsSize) {
this.maxFieldsSize = options.maxFieldsSize;
}
// AWS specific options
// See http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property
const awsOptionNames = [
'StorageClass',
'CacheControl',
'ServerSideEncryption',
'SSEKMSKeyId',
'SSECustomerAlgorithm',
'SSECustomerKey',
'SSECustomerKeyMD5',
];
for (const awsOption of awsOptionNames) {
if (typeof options[awsOption] !== 'undefined') {
this[awsOption] = options[awsOption];
}
}
}
function map(obj) {
@ -82,11 +63,8 @@ function map(obj) {
* @callback {Function} callback Callback function
* @param {Object|String} err Error string or object
* @param {Object[]} containers An array of container metadata objects
* @promise
*/
StorageService.prototype.getContainers = function(cb) {
cb = cb || utils.createPromiseCallback();
this.client.getContainers(function(err, containers) {
if (err) {
cb(err, containers);
@ -96,8 +74,6 @@ StorageService.prototype.getContainers = function(cb) {
}));
}
});
return cb.promise;
};
/**
@ -108,24 +84,18 @@ StorageService.prototype.getContainers = function(cb) {
* @callback {Function} cb Callback function
* @param {Object|String} err Error string or object
* @param {Object} container Container metadata object
* @promise
*/
StorageService.prototype.createContainer = function(options, cb) {
options = options || {};
cb = cb || utils.createPromiseCallback();
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);
}
debug('Creating container with options %o', options);
this.client.createContainer(options, function(err, container) {
return this.client.createContainer(options, function(err, container) {
return cb(err, map(container));
});
return cb.promise;
};
/**
@ -133,13 +103,9 @@ StorageService.prototype.createContainer = function(options, cb) {
* @param {String} container Container name.
* @callback {Function} callback Callback function.
* @param {Object|String} err Error string or object
* @promise
*/
StorageService.prototype.destroyContainer = function(container, cb) {
cb = cb || utils.createPromiseCallback();
this.client.destroyContainer(container, cb);
return cb.promise;
return this.client.destroyContainer(container, cb);
};
/**
@ -148,20 +114,15 @@ StorageService.prototype.destroyContainer = function(container, cb) {
* @callback {Function} callback Callback function.
* @param {Object|String} err Error string or object
* @param {Object} container Container metadata object
* @promise
*/
StorageService.prototype.getContainer = function(container, cb) {
cb = cb || utils.createPromiseCallback();
this.client.getContainer(container, function(err, container) {
return this.client.getContainer(container, function(err, container) {
if (err && err.code === 'ENOENT') {
err.statusCode = err.status = 404;
return cb(err);
}
return cb(err, map(container));
});
return cb.promise;
};
/**
@ -169,6 +130,8 @@ StorageService.prototype.getContainer = function(container, cb) {
* @param {String} container Container name
* @param {String} file File name
* @options {Object} [options] Options for uploading
* @callback callback Callback function
* @param {String|Object} err Error string or object
* @returns {Stream} Stream for uploading
*/
StorageService.prototype.uploadStream = function(container, file, options) {
@ -182,7 +145,7 @@ StorageService.prototype.uploadStream = function(container, file, options) {
if (file) {
options.remote = file;
}
debug('Obtaining upload stream for file %s and options %o', file, options);
return this.client.upload(options);
};
@ -191,6 +154,8 @@ StorageService.prototype.uploadStream = function(container, file, options) {
* @param {String} container Container name.
* @param {String} file File name.
* @options {Object} options Options for downloading
* @callback {Function} callback Callback function
* @param {String|Object} err Error string or object
* @returns {Stream} Stream for downloading
*/
StorageService.prototype.downloadStream = function(container, file, options) {
@ -204,7 +169,7 @@ StorageService.prototype.downloadStream = function(container, file, options) {
if (file) {
options.remote = file;
}
debug('Obtaining download stream for file %s and options %o', file, options);
return this.client.download(options);
};
@ -215,7 +180,6 @@ StorageService.prototype.downloadStream = function(container, file, options) {
* @callback {Function} cb Callback function
* @param {Object|String} err Error string or object
* @param {Object[]} files An array of file metadata objects
* @promise
*/
StorageService.prototype.getFiles = function(container, options, cb) {
if (typeof options === 'function' && !cb) {
@ -223,10 +187,7 @@ StorageService.prototype.getFiles = function(container, options, cb) {
cb = options;
options = {};
}
cb = cb || utils.createPromiseCallback();
this.client.getFiles(container, options, function(err, files) {
return this.client.getFiles(container, options, function(err, files) {
if (err) {
cb(err, files);
} else {
@ -235,8 +196,6 @@ StorageService.prototype.getFiles = function(container, options, cb) {
}));
}
});
return cb.promise;
};
/**
@ -246,20 +205,11 @@ StorageService.prototype.getFiles = function(container, options, cb) {
* @callback {Function} cb Callback function
* @param {Object|String} err Error string or object
* @param {Object} file File metadata object
* @promise
*/
StorageService.prototype.getFile = function(container, file, cb) {
cb = cb || utils.createPromiseCallback();
this.client.getFile(container, file, function(err, f) {
if (err && err.code === 'ENOENT') {
err.statusCode = err.status = 404;
return cb(err);
}
return this.client.getFile(container, file, function(err, f) {
return cb(err, map(f));
});
return cb.promise;
};
/**
@ -268,41 +218,23 @@ StorageService.prototype.getFile = function(container, file, cb) {
* @param {String} file File name
* @callback {Function} cb Callback function
* @param {Object|String} err Error string or object
* @promise
*/
StorageService.prototype.removeFile = function(container, file, cb) {
cb = cb || utils.createPromiseCallback();
this.client.removeFile(container, file, cb);
return cb.promise;
return this.client.removeFile(container, file, cb);
};
/**
* Upload middleware for the HTTP request/response <!-- Should this be documented? -->
* @param {String} [container] Container name
* @param {Request} req Request object
* @param {Response} res Response object
* @param {Object} [options] Options for upload
* @param {Function} cb Callback function
* @promise
*/
StorageService.prototype.upload = function(container, req, res, options, cb) {
debug('Configuring upload with options %o', options);
// 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;
}
StorageService.prototype.upload = function(req, res, options, cb) {
if (!cb && 'function' === typeof options) {
cb = options;
options = {};
}
cb = cb || utils.createPromiseCallback();
if (this.getFilename && !options.getFilename) {
options.getFilename = this.getFilename;
}
@ -321,32 +253,7 @@ StorageService.prototype.upload = function(container, req, res, options, cb) {
if (this.maxFieldsSize && !options.maxFieldsSize) {
options.maxFieldsSize = this.maxFieldsSize;
}
// AWS specific options
// See http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property
const awsOptionNames = [
'StorageClass',
'CacheControl',
'ServerSideEncryption',
'SSEKMSKeyId',
'SSECustomerAlgorithm',
'SSECustomerKey',
'SSECustomerKeyMD5',
];
for (const awsOption of awsOptionNames) {
if (this[awsOption] && !options[awsOption]) {
options[awsOption] = this[awsOption];
}
}
if (typeof container === 'string') {
options.container = container;
}
debug('Upload configured with options %o', options);
handler.upload(this.client, req, res, options, cb);
return cb.promise;
return handler.upload(this.client, req, res, options, cb);
};
/**
@ -356,13 +263,9 @@ StorageService.prototype.upload = function(container, req, res, options, cb) {
* @param {Request} req HTTP request
* @param {Response} res HTTP response
* @param {Function} cb Callback function
* @promise
*/
StorageService.prototype.download = function(container, file, req, res, cb) {
cb = cb || utils.createPromiseCallback();
handler.download(this.client, req, res, container, file, cb);
return cb.promise;
return handler.download(this.client, req, res, container, file, cb);
};
StorageService.modelName = 'storage';
@ -379,7 +282,7 @@ StorageService.prototype.getContainers.http =
StorageService.prototype.getContainer.shared = true;
StorageService.prototype.getContainer.accepts = [
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
{arg: 'container', type: 'string', required: true},
];
StorageService.prototype.getContainer.returns = {
arg: 'container',
@ -401,7 +304,7 @@ StorageService.prototype.createContainer.http =
StorageService.prototype.destroyContainer.shared = true;
StorageService.prototype.destroyContainer.accepts = [
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
{arg: 'container', type: 'string'},
];
StorageService.prototype.destroyContainer.returns = {};
StorageService.prototype.destroyContainer.http =
@ -409,7 +312,7 @@ StorageService.prototype.destroyContainer.http =
StorageService.prototype.getFiles.shared = true;
StorageService.prototype.getFiles.accepts = [
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
{arg: 'container', type: 'string', required: true},
];
StorageService.prototype.getFiles.returns = {arg: 'files', type: 'array', root: true};
StorageService.prototype.getFiles.http =
@ -417,8 +320,8 @@ StorageService.prototype.getFiles.http =
StorageService.prototype.getFile.shared = true;
StorageService.prototype.getFile.accepts = [
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
{arg: 'file', type: 'string', required: true, 'http': {source: 'path'}},
{arg: 'container', type: 'string', required: true},
{arg: 'file', type: 'string', required: true},
];
StorageService.prototype.getFile.returns = {arg: 'file', type: 'object', root: true};
StorageService.prototype.getFile.http =
@ -426,8 +329,8 @@ StorageService.prototype.getFile.http =
StorageService.prototype.removeFile.shared = true;
StorageService.prototype.removeFile.accepts = [
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
{arg: 'file', type: 'string', required: true, 'http': {source: 'path'}},
{arg: 'container', type: 'string', required: true},
{arg: 'file', type: 'string', required: true},
];
StorageService.prototype.removeFile.returns = {};
StorageService.prototype.removeFile.http =
@ -435,7 +338,6 @@ StorageService.prototype.removeFile.http =
StorageService.prototype.upload.shared = true;
StorageService.prototype.upload.accepts = [
{arg: 'container', type: 'string', required: true, 'http': {source: 'path'}},
{arg: 'req', type: 'object', 'http': {source: 'req'}},
{arg: 'res', type: 'object', 'http': {source: 'res'}},
];

View File

@ -1,20 +0,0 @@
// Copyright IBM Corp. 2018,2019. All Rights Reserved.
// 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';
exports.createPromiseCallback = createPromiseCallback;
function createPromiseCallback() {
var cb;
var promise = new Promise(function(resolve, reject) {
cb = function(err, data) {
if (err) return reject(err);
return resolve(data);
};
});
cb.promise = promise;
return cb;
}

View File

@ -2,9 +2,9 @@
"name": "loopback-component-storage",
"description": "Loopback Storage Service",
"engines": {
"node": ">=8"
"node": ">=4"
},
"version": "3.7.0",
"version": "3.3.0",
"main": "index.js",
"scripts": {
"lint": "eslint .",
@ -12,27 +12,25 @@
"posttest": "npm run lint"
},
"dependencies": {
"async": "^2.6.1",
"debug": "^3.1.0",
"formidable": "^1.2.1",
"pkgcloud": "^2.1.1",
"strong-globalize": "^4.1.1",
"uuid": "^3.2.1"
"async": "^2.1.5",
"formidable": "^1.0.16",
"pkgcloud": "^1.1.0",
"strong-globalize": "^2.6.2",
"uuid": "^3.0.1"
},
"devDependencies": {
"eslint": "^5.4.0",
"eslint-config-loopback": "^11.0.0",
"express": "^4.16.3",
"loopback": "^3.22.1",
"mkdirp": "^0.5.1",
"mocha": "^5.2.0",
"supertest": "^3.1.0",
"semver": "^5.5.0"
"eslint": "^3.17.1",
"eslint-config-loopback": "^8.0.0",
"express": "^4.11.0",
"loopback": "^3.0.0",
"mkdirp": "^0.5.0",
"mocha": "^3.2.0",
"supertest": "^3.0.0",
"semver": "^5.3.0"
},
"repository": {
"type": "git",
"url": "https://github.com/strongloop/loopback-component-storage.git"
},
"license": "Artistic-2.0",
"author": "IBM Corp."
"license": "Artistic-2.0"
}

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Copyright IBM Corp. 2016. All Rights Reserved.
// 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');

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
// Copyright IBM Corp. 2013,2014. All Rights Reserved.
// 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;
@ -25,32 +24,13 @@ describe('FileSystem based storage provider', function() {
describe('container apis', function() {
var client = null;
it('should require an existing directory as the root', function(done) {
client = new FileSystemProvider({
root: path.join(__dirname, 'storage'),
});
client = new FileSystemProvider({root: path.join(__dirname, 'storage')});
process.nextTick(done);
});
it('should work even it is ran from other path', function(done) {
process.chdir('../../../');
try {
console.log(`running from ${process.cwd()}`);
client = new FileSystemProvider({
root: path.join(__dirname, 'storage'),
});
process.nextTick(done);
} catch (error) {
process.nextTick(done(error));
}
});
it('should complain if the root directory doesn\'t exist', function(done) {
try {
client = new FileSystemProvider({
root: path.join(__dirname, '_storage'),
});
client = new FileSystemProvider({root: path.join(__dirname, '_storage')});
process.nextTick(done.bind(null, 'Error'));
} catch (err) {
// Should be here
@ -61,20 +41,11 @@ describe('FileSystem based storage provider', function() {
it('should return an empty list of containers', function(done) {
client.getContainers(function(err, containers) {
assert(!err);
assert.equal(containers.length, 0);
assert.equal(0, containers.length);
done(err, containers);
});
});
it('should return an empty list of containers - promise', function(done) {
client.getContainers()
.then(function(containers) {
assert.equal(containers.length, 0);
done();
})
.catch(done);
});
it('should create a new container', function(done) {
client.createContainer({name: 'c1'}, function(err, container) {
assert(!err);
@ -83,15 +54,6 @@ describe('FileSystem based storage provider', function() {
});
});
it('should create a new container - promise', function(done) {
client.createContainer({name: 'c3'})
.then(function(container) {
verifyMetadata(container, 'c3');
done();
})
.catch(done);
});
it('should get a container c1', function(done) {
client.getContainer('c1', function(err, container) {
assert(!err);
@ -100,15 +62,6 @@ describe('FileSystem based storage provider', function() {
});
});
it('should get a container c1 - promise', function(done) {
client.getContainer('c1')
.then(function(container) {
verifyMetadata(container, 'c1');
done();
})
.catch(done);
});
it('should not get a container c2', function(done) {
client.getContainer('c2', function(err, container) {
assert(err);
@ -116,18 +69,10 @@ describe('FileSystem based storage provider', function() {
});
});
it('should destroy a container c3 - promise', function(done) {
client.destroyContainer('c3')
.then(function(container) {
done(null, container);
})
.catch(done);
});
it('should return one container', function(done) {
client.getContainers(function(err, containers) {
assert(!err);
assert.equal(containers.length, 1);
assert.equal(1, containers.length);
done(err, containers);
});
});
@ -149,9 +94,7 @@ describe('FileSystem based storage provider', function() {
describe('file apis', function() {
var fs = require('fs');
var client = new FileSystemProvider({
root: path.join(__dirname, 'storage'),
});
var client = new FileSystemProvider({root: path.join(__dirname, 'storage')});
it('should create a new container', function(done) {
client.createContainer({name: 'c1'}, function(err, container) {
@ -167,7 +110,6 @@ describe('FileSystem based storage provider', function() {
writer.on('error', done);
});
/* eslint-disable mocha/handle-done-callback */
it('should fail to upload a file with invalid characters', function(done) {
var writer = client.upload({container: 'c1', remote: 'a/f1.txt'});
fs.createReadStream(path.join(__dirname, 'files/f1.txt')).pipe(writer);
@ -182,26 +124,20 @@ describe('FileSystem based storage provider', function() {
cb = clearCb;
});
});
/* eslint-enable mocha/handle-done-callback */
it('should download a file', function(done) {
var reader = client.download({
container: 'c1',
remote: 'f1.txt',
});
reader.pipe(
fs.createWriteStream(path.join(__dirname, 'files/f1_downloaded.txt'))
);
reader.pipe(fs.createWriteStream(path.join(__dirname, 'files/f1_downloaded.txt')));
reader.on('end', done);
reader.on('error', done);
});
/* eslint-disable mocha/handle-done-callback */
it('should fail to download a file with invalid characters', function(done) {
var reader = client.download({container: 'c1', remote: 'a/f1.txt'});
reader.pipe(
fs.createWriteStream(path.join(__dirname, 'files/a-f1_downloaded.txt'))
);
reader.pipe(fs.createWriteStream(path.join(__dirname, 'files/a-f1_downloaded.txt')));
var cb = done;
var clearCb = function() {};
reader.on('error', function() {
@ -213,25 +149,15 @@ describe('FileSystem based storage provider', function() {
cb = clearCb;
});
});
/* eslint-enable mocha/handle-done-callback */
it('should get files for a container', function(done) {
client.getFiles('c1', function(err, files) {
assert(!err);
assert.equal(files.length, 1);
assert.equal(1, files.length);
done(err, files);
});
});
it('should get files for a container - promise', function(done) {
client.getFiles('c1')
.then(function(files) {
assert.equal(files.length, 1);
done();
})
.catch(done);
});
it('should get a file', function(done) {
client.getFile('c1', 'f1.txt', function(err, f) {
assert(!err);
@ -241,16 +167,6 @@ describe('FileSystem based storage provider', function() {
});
});
it('should get a file - promise', function(done) {
client.getFile('c1', 'f1.txt')
.then(function(f) {
assert.ok(f);
verifyMetadata(f, 'f1.txt');
done();
})
.catch(done);
});
it('should remove a file', function(done) {
client.removeFile('c1', 'f1.txt', function(err) {
assert(!err);
@ -258,67 +174,20 @@ describe('FileSystem based storage provider', function() {
});
});
it('should remove a file - promise', function(done) {
createFile('c1', 'f1.txt').then(function() {
return client.removeFile('c1', 'f1.txt')
.then(function() {
done();
});
})
.catch(done);
});
it('should get no files from a container', function(done) {
client.getFiles('c1', function(err, files) {
assert(!err);
assert.equal(files.length, 0);
assert.equal(0, files.length);
done(err, files);
});
});
it('should get no files from a container - promise', function(done) {
client.getFiles('c1')
.then(function(files) {
assert.equal(files.length, 0);
done();
})
.catch(done);
});
it('should not get a file from a container', function(done) {
client.getFile('c1', 'f2.txt', function(err, f) {
assert(err);
assert.equal(err.code, 'ENOENT');
assert(!f);
done();
});
});
it('should not get a file from a container - promise', function(done) {
client.getFile('c1', 'f2.txt')
.then(function() {
throw new Error('should not be throw');
})
.catch(function(err) {
assert.equal(err.code, 'ENOENT');
done();
});
});
it('should destroy a container c1', function(done) {
client.destroyContainer('c1', function(err, container) {
// console.error(err);
assert(!err);
done(err, container);
});
});
function createFile(container, file) {
return new Promise(function(resolve, reject) {
var writer = client.upload({container: container, remote: file});
fs.createReadStream(path.join(__dirname, 'files/f1.txt')).pipe(writer);
writer.on('finish', resolve);
writer.on('error', reject);
});
}
});
});

View File

@ -1,4 +1,3 @@
test.jpg
image-*.jpg
customimagefield_test.jpg
customimagefield1_test.jpg
customimagefield_test.jpg

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2013,2019. All Rights Reserved.
// Copyright IBM Corp. 2013,2015. All Rights Reserved.
// 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');
@ -20,20 +19,11 @@ describe('Storage service', function() {
it('should return an empty list of containers', function(done) {
storageService.getContainers(function(err, containers) {
assert(!err);
assert.equal(containers.length, 0);
assert.equal(0, containers.length);
done(err, containers);
});
});
it('should return an empty list of containers - promise', function(done) {
storageService.getContainers()
.then(function(containers) {
assert.equal(containers.length, 0);
done();
})
.catch(done);
});
it('should create a new container', function(done) {
storageService.createContainer({name: 'c1'}, function(err, container) {
assert(!err);
@ -42,15 +32,6 @@ describe('Storage service', function() {
});
});
it('should create a new container - promise', function(done) {
storageService.createContainer({name: 'c3'})
.then(function(container) {
assert(container.getMetadata());
done();
})
.catch(done);
});
it('should get a container c1', function(done) {
storageService.getContainer('c1', function(err, container) {
assert(!err);
@ -59,15 +40,6 @@ describe('Storage service', function() {
});
});
it('should get a container c1 - promise', function(done) {
storageService.getContainer('c1')
.then(function(container) {
assert(container.getMetadata());
done();
})
.catch(done);
});
it('should not get a container c2', function(done) {
storageService.getContainer('c2', function(err, container) {
assert(err);
@ -75,18 +47,10 @@ describe('Storage service', function() {
});
});
it('should destroy a container c3 - promise', function(done) {
storageService.destroyContainer('c3')
.then(function(container) {
done(null, container);
})
.catch(done);
});
it('should return one container', function(done) {
storageService.getContainers(function(err, containers) {
assert(!err);
assert.equal(containers.length, 1);
assert.equal(1, containers.length);
done(err, containers);
});
});
@ -140,20 +104,11 @@ describe('Storage service', function() {
it('should get files for a container', function(done) {
storageService.getFiles('c1', function(err, files) {
assert(!err);
assert.equal(files.length, 1);
assert.equal(1, files.length);
done(err, files);
});
});
it('should get files for a container - promise', function(done) {
storageService.getFiles('c1')
.then(function(files) {
assert.equal(files.length, 1);
done();
})
.catch(done);
});
it('should get a file', function(done) {
storageService.getFile('c1', 'f1.txt', function(err, f) {
assert(!err);
@ -163,16 +118,6 @@ describe('Storage service', function() {
});
});
it('should get a file - promise', function(done) {
storageService.getFile('c1', 'f1.txt')
.then(function(f) {
assert.ok(f);
assert(f.getMetadata());
done();
})
.catch(done);
});
it('should remove a file', function(done) {
storageService.removeFile('c1', 'f1.txt', function(err) {
assert(!err);
@ -180,61 +125,21 @@ describe('Storage service', function() {
});
});
it('should remove a file - promise', function(done) {
createFile('c1', 'f1.txt')
.then(function() {
return storageService.removeFile('c1', 'f1.txt')
.then(function() {
done();
});
})
.catch(done);
});
it('should get no files from a container', function(done) {
storageService.getFiles('c1', function(err, files) {
assert(!err);
assert.equal(files.length, 0);
assert.equal(0, files.length);
done(err, files);
});
});
it('should not get a file from a container', function(done) {
storageService.getFile('c1', 'f1.txt', function(err, f) {
assert(err);
assert.equal(err.code, 'ENOENT');
assert.equal(err.status, 404);
assert(!f);
done();
});
});
it('should not get a file from a container - promise', function(done) {
storageService.getFile('c1', 'f1.txt')
.then(function() {
throw new Error('should not be throw');
})
.catch(function(err) {
assert.equal(err.code, 'ENOENT');
assert.equal(err.status, 404);
done();
});
});
it('should destroy a container c1', function(done) {
storageService.destroyContainer('c1', function(err, container) {
// console.error(err);
assert(!err);
done(err, container);
});
});
function createFile(container, file) {
return new Promise(function(resolve, reject) {
var writer = storageService.uploadStream(container, file);
fs.createReadStream(path.join(__dirname, 'files/f1.txt')).pipe(writer);
writer.on('finish', resolve);
writer.on('error', reject);
});
}
});
});

View File

@ -1,8 +1,7 @@
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// 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');
@ -33,23 +32,6 @@ 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
app.use(loopback.rest());
@ -200,27 +182,14 @@ describe('storage service', function() {
.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',
field: 'image',
size: 60475,
},
],
},
fields: {},
},
});
assert.deepEqual(res.body, {'result': {'files': {'image': [
{'container': 'album1', 'name': 'test.jpg', 'type': 'image/jpeg', 'field': 'image', 'size': 60475},
]}, 'fields': {}}});
done();
});
});
it('fails to upload using dotdot file path (1)', function(done) {
it('fails to upload using dotdot file path', function(done) {
request('http://localhost:' + app.get('port'))
.post('/containers/%2e%2e/upload')
.expect(200, function(err, res) {
@ -229,7 +198,7 @@ describe('storage service', function() {
});
});
it('fails to upload using dotdot file path (2)', function(done) {
it('fails to upload using dotdot file path', function(done) {
request('http://localhost:' + app.get('port'))
.post('%2e%2e/containers/upload')
.expect(200, function(err, res) {
@ -238,7 +207,7 @@ describe('storage service', function() {
});
});
it('fails to upload using dotdot file path (3)', function(done) {
it('fails to upload using dotdot file path', function(done) {
request('http://localhost:' + app.get('port'))
.post('%2e%2e')
.expect(200, function(err, res) {
@ -247,7 +216,7 @@ describe('storage service', function() {
});
});
it('fails to upload using dotdot file path (4)', function(done) {
it('fails to upload using dotdot file path', function(done) {
request('http://localhost:' + app.get('port'))
.post('/containers/upload/%2e%2e')
.expect(200, function(err, res) {
@ -263,24 +232,9 @@ describe('storage service', function() {
.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',
field: 'image',
acl: 'public-read',
size: 60475,
},
],
},
fields: {},
},
});
assert.deepEqual(res.body, {'result': {'files': {'image': [
{'container': 'album1', 'name': 'image-test.jpg', 'originalFilename': 'test.jpg', 'type': 'image/jpeg', 'field': 'image', 'acl': 'public-read', 'size': 60475},
]}, 'fields': {}}});
done();
});
});
@ -320,14 +274,10 @@ describe('storage service', function() {
.set('Connection', 'keep-alive')
.expect('Content-Type', /json/)
.expect(400, function(err, res) {
var indexOfMsg = res.body.error.message
.toLowerCase()
.indexOf('no file');
assert.notEqual(
indexOfMsg,
-1,
'Error message does not contain "no file"'
);
var indexOfMsg =
res.body.error.message.toLowerCase().indexOf('no file');
assert.notEqual(indexOfMsg, -1,
'Error message does not contain \"no file\"');
done(err);
});
} else {
@ -337,10 +287,8 @@ describe('storage service', function() {
.set('Connection', 'keep-alive')
.expect('Content-Type', /json/)
.expect(500, function(err, res) {
assert.equal(
res.body.error.message,
'bad content-type header, no content-type'
);
assert.equal(res.body.error.message,
'bad content-type header, no content-type');
done(err);
});
}
@ -456,62 +404,19 @@ describe('storage service', function() {
});
});
it(
'should upload a file with custom route accessing directly to the ' +
'storage connector with renamer',
function(done) {
request('http://localhost:' + app.get('port'))
.post('/custom/upload')
.attach('customimagefield', 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: {
customimagefield: [
{
container: 'album1',
name: 'customimagefield_test.jpg',
originalFilename: 'test.jpg',
type: 'image/jpeg',
field: 'customimagefield',
size: 60475,
},
],
},
fields: {},
},
});
done();
});
}
);
it('should upload a file with container param', function(done) {
it('should upload a file with custom route accessing directly to the ' +
'storage connector with renamer', function(done) {
request('http://localhost:' + app.get('port'))
.post('/custom/uploadWithContainer')
.attach('customimagefield1', path.join(__dirname, './fixtures/test.jpg'))
.post('/custom/upload')
.attach('customimagefield', 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: {},
},
});
assert.deepEqual(res.body, {'result': {'files': {'customimagefield': [
{'container': 'album1', 'name': 'customimagefield_test.jpg',
'originalFilename': 'test.jpg', 'type': 'image/jpeg',
'field': 'customimagefield', 'size': 60475},
]}, 'fields': {}}});
done();
});
});