feat(#127): adding multiple file upload

- prepare www interface
- implement options validation
- implement specs for js interface
- improve error messages
This commit is contained in:
Sefa Ilkimen
2019-09-26 16:37:19 +02:00
parent e17768041c
commit ae3e821639
4 changed files with 98 additions and 27 deletions
+33 -3
View File
@@ -129,7 +129,7 @@ describe('Advanced HTTP public interface', function () {
});
it('throws an Error when you try to add a cookie by using "setHeader" #46', () => {
(function () { http.setHeader('*', 'cookie', 'value'); }).should.throw();
(() => { http.setHeader('*', 'cookie', 'value'); }).should.throw(messages.ADDING_COOKIES_NOT_SUPPORTED);
});
it('configures global timeout value correctly with given valid value', () => {
@@ -138,7 +138,7 @@ describe('Advanced HTTP public interface', function () {
});
it('throws an Error when you try to configure global timeout with a string', () => {
(function () { http.setRequestTimeout('myString'); }).should.throw(messages.INVALID_TIMEOUT_VALUE);
(() => { http.setRequestTimeout('myString'); }).should.throw(messages.INVALID_TIMEOUT_VALUE);
});
it('sets global option for following redirects correctly', () => {
@@ -147,7 +147,7 @@ describe('Advanced HTTP public interface', function () {
});
it('throws an Error when you try to configure global option for following redirects with a string', () => {
(function () { http.setFollowRedirect('myString'); }).should.throw(messages.INVALID_FOLLOW_REDIRECT_VALUE);
(() => { http.setFollowRedirect('myString'); }).should.throw(messages.INVALID_FOLLOW_REDIRECT_VALUE);
});
it('exposes an enumeration style object with mappings for the error codes', () => {
@@ -475,4 +475,34 @@ describe('Common helpers', function () {
handler({ data: 'NotValidJson' });
});
});
describe('checkFileOptions()', function() {
const jsUtil = require('../www/js-util');
const messages = require('../www/messages');
const helpers = require('../www/helpers')(jsUtil, null, messages, null, null);
it('checks valid file options correctly', () => {
const opts = {
filePaths: ['file://path/to/file.png'],
names: ['ScreenCapture']
};
helpers.checkFileOptions(opts.filePaths, opts.names).should.be.eql(opts);
});
it('throws an error when file options are missing', () => {
(() => helpers.checkFileOptions(undefined, ['ScreenCapture'])).should.throw(messages.FILE_PATHS_TYPE_MISMATCH);
(() => helpers.checkFileOptions(['file://path/to/file.png'], undefined)).should.throw(messages.NAMES_TYPE_MISMATCH);
});
it('throws an error when file options contains empty arrays', () => {
(() => helpers.checkFileOptions([], ['ScreenCapture'])).should.throw(messages.EMPTY_FILE_PATHS);
(() => helpers.checkFileOptions(['file://path/to/file.png'], [])).should.throw(messages.EMPTY_NAMES);
});
it('throws an error when file options contains invalid values', () => {
(() => helpers.checkFileOptions([1], ['ScreenCapture'])).should.throw(messages.FILE_PATHS_TYPE_MISMATCH);
(() => helpers.checkFileOptions(['file://path/to/file.png'], [1])).should.throw(messages.NAMES_TYPE_MISMATCH);
});
});
})
+44 -14
View File
@@ -7,21 +7,22 @@ module.exports = function init(jsUtil, cookieHandler, messages, base64, errorCod
var interface = {
b64EncodeUnicode: b64EncodeUnicode,
checkSerializer: checkSerializer,
checkSSLCertMode: checkSSLCertMode,
checkClientAuthMode: checkClientAuthMode,
checkClientAuthOptions: checkClientAuthOptions,
checkFileOptions: checkFileOptions,
checkFollowRedirectValue: checkFollowRedirectValue,
checkForBlacklistedHeaderKey: checkForBlacklistedHeaderKey,
checkForInvalidHeaderValue: checkForInvalidHeaderValue,
checkSerializer: checkSerializer,
checkSSLCertMode: checkSSLCertMode,
checkTimeoutValue: checkTimeoutValue,
checkFollowRedirectValue: checkFollowRedirectValue,
injectCookieHandler: injectCookieHandler,
injectRawResponseHandler: injectRawResponseHandler,
injectFileEntryHandler: injectFileEntryHandler,
getMergedHeaders: getMergedHeaders,
getProcessedData: getProcessedData,
handleMissingCallbacks: handleMissingCallbacks,
handleMissingOptions: handleMissingOptions
handleMissingOptions: handleMissingOptions,
injectCookieHandler: injectCookieHandler,
injectFileEntryHandler: injectFileEntryHandler,
injectRawResponseHandler: injectRawResponseHandler,
};
// expose all functions for testing purposes
@@ -94,6 +95,20 @@ module.exports = function init(jsUtil, cookieHandler, messages, base64, errorCod
return obj;
}
function checkArray(array, allowedDataTypes, onInvalidValueMessage) {
if (jsUtil.getTypeOf(array) !== 'Array') {
throw new Error(onInvalidValueMessage);
}
for (var i = 0; i < array.length; ++i) {
if (allowedDataTypes.indexOf(jsUtil.getTypeOf(array[i])) === -1) {
throw new Error(onInvalidValueMessage);
}
}
return array;
}
function checkHttpMethod(method) {
return checkForValidStringValue(validHttpMethods, method, messages.INVALID_HTTP_METHOD);
}
@@ -172,7 +187,7 @@ module.exports = function init(jsUtil, cookieHandler, messages, base64, errorCod
function checkForInvalidHeaderValue(value) {
if (jsUtil.getTypeOf(value) !== 'String') {
throw new Error(messages.INVALID_HEADERS_VALUE);
throw new Error(messages.INVALID_HEADER_VALUE);
}
return value;
@@ -195,11 +210,28 @@ module.exports = function init(jsUtil, cookieHandler, messages, base64, errorCod
}
function checkHeadersObject(headers) {
return checkKeyValuePairObject(headers, ['String'], messages.INVALID_HEADERS_VALUE);
return checkKeyValuePairObject(headers, ['String'], messages.TYPE_MISMATCH_HEADERS);
}
function checkParamsObject(params) {
return checkKeyValuePairObject(params, ['String', 'Array'], messages.INVALID_PARAMS_VALUE);
return checkKeyValuePairObject(params, ['String', 'Array'], messages.TYPE_MISMATCH_PARAMS);
}
function checkFileOptions(filePaths, names) {
var opts = {
filePaths: checkArray(filePaths, ['String'], messages.TYPE_MISMATCH_FILE_PATHS),
names: checkArray(names, ['String'], messages.TYPE_MISMATCH_NAMES)
};
if (!opts.filePaths.length) {
throw new Error(messages.EMPTY_FILE_PATHS);
}
if (!opts.names.length) {
throw new Error(messages.EMPTY_NAMES);
}
return opts;
}
function resolveCookieString(headers) {
@@ -324,7 +356,7 @@ module.exports = function init(jsUtil, cookieHandler, messages, base64, errorCod
var allowedDataTypes = getAllowedDataTypes(dataSerializer);
if (allowedDataTypes.indexOf(currentDataType) === -1) {
throw new Error(messages.DATA_TYPE_MISMATCH + ' ' + allowedDataTypes.join(', '));
throw new Error(messages.TYPE_MISMATCH_DATA + ' ' + allowedDataTypes.join(', '));
}
if (dataSerializer === 'utf8') {
@@ -355,9 +387,7 @@ module.exports = function init(jsUtil, cookieHandler, messages, base64, errorCod
followRedirect: checkFollowRedirectValue(options.followRedirect || globals.followRedirect),
headers: checkHeadersObject(options.headers || {}),
params: checkParamsObject(options.params || {}),
data: jsUtil.getTypeOf(options.data) === 'Undefined' ? null : options.data,
filePath: options.filePath || '',
name: options.name || ''
data: jsUtil.getTypeOf(options.data) === 'Undefined' ? null : options.data
};
}
};
+14 -9
View File
@@ -1,20 +1,25 @@
module.exports = {
ADDING_COOKIES_NOT_SUPPORTED: 'advanced-http: "setHeader" does not support adding cookies, please use "setCookie" function instead',
DATA_TYPE_MISMATCH: 'advanced-http: "data" argument supports only following data types:',
INVALID_CLIENT_AUTH_ALIAS: 'advanced-http: invalid client certificate alias, needs to be a string or undefined',
EMPTY_FILE_PATHS: 'advanced-http: "filePaths" option array must not be empty, <filePaths: string[]>',
EMPTY_NAMES: 'advanced-http: "names" option array must not be empty, <names: string[]>',
INVALID_CLIENT_AUTH_ALIAS: 'advanced-http: invalid client certificate alias, needs to be a string or undefined, <alias: string | undefined>',
INVALID_CLIENT_AUTH_MODE: 'advanced-http: invalid client certificate authentication mode, supported modes are:',
INVALID_CLIENT_AUTH_OPTIONS: 'advanced-http: invalid client certificate authentication options, needs to be an object',
INVALID_CLIENT_AUTH_PKCS_PASSWORD: 'advanced-http: invalid PKCS12 container password, needs to be a string',
INVALID_CLIENT_AUTH_RAW_PKCS: 'advanced-http: invalid PKCS12 container, needs to be an array buffer',
INVALID_CLIENT_AUTH_OPTIONS: 'advanced-http: invalid client certificate authentication options, needs to be an dictionary style object',
INVALID_CLIENT_AUTH_PKCS_PASSWORD: 'advanced-http: invalid PKCS12 container password, needs to be a string, <pkcsPassword: string>',
INVALID_CLIENT_AUTH_RAW_PKCS: 'advanced-http: invalid PKCS12 container, needs to be an array buffer, <rawPkcs: ArrayBuffer>',
INVALID_DATA_SERIALIZER: 'advanced-http: invalid serializer, supported serializers are:',
INVALID_FOLLOW_REDIRECT_VALUE: 'advanced-http: invalid follow redirect value, needs to be a boolean value',
INVALID_HEADERS_VALUE: 'advanced-http: header values must be strings',
INVALID_FOLLOW_REDIRECT_VALUE: 'advanced-http: invalid follow redirect value, needs to be a boolean value, <followRedirect: boolean>',
INVALID_HEADER_VALUE: 'advanced-http: invalid header value, needs to be a string, <header: string>',
INVALID_HTTP_METHOD: 'advanced-http: invalid HTTP method, supported methods are:',
INVALID_PARAMS_VALUE: 'advanced-http: invalid params object, needs to be an object with strings',
INVALID_RESPONSE_TYPE: 'advanced-http: invalid response type, supported types are:',
INVALID_SSL_CERT_MODE: 'advanced-http: invalid SSL cert mode, supported modes are:',
INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value',
INVALID_TIMEOUT_VALUE: 'advanced-http: invalid timeout value, needs to be a positive numeric value, <timeout: number>',
MANDATORY_FAIL: 'advanced-http: missing mandatory "onFail" callback function',
MANDATORY_SUCCESS: 'advanced-http: missing mandatory "onSuccess" callback function',
POST_PROCESSING_FAILED: 'advanced-http: an error occured during post processing response:',
TYPE_MISMATCH_DATA: 'advanced-http: "data" option supports only following data types:',
TYPE_MISMATCH_FILE_PATHS: 'advanced-http: "filePaths" option needs to be an string array, <filePaths: string[]>',
TYPE_MISMATCH_HEADERS: 'advanced-http: "headers" option needs to be an dictionary style object, <headers: {[key: string]: string}>',
TYPE_MISMATCH_NAMES: 'advanced-http: "names" option needs to be an string array, <names: string[]>',
TYPE_MISMATCH_PARAMS: 'advanced-http: "params" option needs to be an dictionary style object, <params: {[key: string]: string | string[]}>',
};
+7 -1
View File
@@ -28,6 +28,7 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
delete: del,
head: head,
uploadFile: uploadFile,
uploadFiles: uploadFiles,
downloadFile: downloadFile,
ErrorCode: errorCodes
};
@@ -155,7 +156,8 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
var data = helpers.getProcessedData(options.data, options.serializer);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', options.method, [url, data, options.serializer, headers, options.timeout, options.followRedirect, options.responseType]);
case 'upload':
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, headers, options.filePath, options.name, options.timeout, options.followRedirect, options.responseType]);
var fileOptions = helpers.checkFileOptions(options.filePaths || [options.filePath], options.names || [options.name]);
return exec(onSuccess, onFail, 'CordovaHttpPlugin', 'uploadFile', [url, headers, fileOptions.filePaths, fileOptions.names, options.timeout, options.followRedirect, options.responseType]);
case 'download':
var onDownloadSuccess = helpers.injectCookieHandler(url, helpers.injectFileEntryHandler(success));
return exec(onDownloadSuccess, onFail, 'CordovaHttpPlugin', 'downloadFile', [url, headers, options.filePath, options.timeout, options.followRedirect]);
@@ -192,6 +194,10 @@ module.exports = function init(exec, cookieHandler, urlUtil, helpers, globalConf
return publicInterface.sendRequest(url, { method: 'upload', params: params, headers: headers, filePath: filePath, name: name }, success, failure);
}
function uploadFiles(url, params, headers, filePaths, names, success, failure) {
return publicInterface.sendRequest(url, { method: 'upload', params: params, headers: headers, filePaths: filePaths, names: names }, success, failure);
}
function downloadFile(url, params, headers, filePath, success, failure) {
return publicInterface.sendRequest(url, { method: 'download', params: params, headers: headers, filePath: filePath }, success, failure);
}