azure-storage
Version:
Microsoft Azure Storage Client Library for Node.js
560 lines (474 loc) • 15.2 kB
JavaScript
//
// Copyright (c) Microsoft and contributors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
var _ = require('underscore');
var util = require('util');
var constants = require('./../util/constants');
var blobConstants = constants.BlobConstants;
var BlobUtilities = require('./../../services/blob/blobutilities');
var FileUtilities = require('./../../services/file/fileutilities');
var azureutil = require('./util');
var SR = require('./sr');
var check = require('validator');
var errors = require('../errors/errors');
var ArgumentError = errors.ArgumentError;
var ArgumentNullError = errors.ArgumentNullError;
exports = module.exports;
function initCallback(callbackParam, resultsCb) {
var fail;
if (callbackParam) {
fail = function (err) {
callbackParam(err);
return false;
};
} else {
fail = function (err) {
throw err;
};
callbackParam = function () {};
}
resultsCb(fail, callbackParam);
}
/**
* Checks if the given value is a valid enumeration or not.
*
* @param {object} value The value to validate.
* @param {object} list The enumeration values.
* @return {boolean}
*/
exports.isValidEnumValue = function (value, list, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
if (!list.some(function (current) {
return current.toLowerCase() === value.toLowerCase();
})) {
return fail(new RangeError(util.format('Invalid value: %s. Options are: %s.', value, list)));
}
callback();
return true;
};
/**
* Creates a anonymous function that check if the given uri is valid or not.
*
* @param {string} uri The uri to validate.
* @return {boolean}
*/
exports.isValidUri = function (uri) {
if (!check.isURL(uri)){
throw new URIError('The provided URI "' + uri + '" is invalid.');
}
return true;
};
/**
* Checks if the given host is valid or not.
*
* @param {string|object} host The host to validate.
* @return {boolean}
*/
exports.isValidHost= function (host) {
if (azureutil.objectIsNull(host)) {
throw new ArgumentNullError('host', SR.STORAGE_HOST_LOCATION_REQUIRED);
} else {
var storageHost = {};
storageHost.primaryHost = _.isString(host) ? host : host.primaryHost;
if (storageHost.primaryHost && !check.isURL(storageHost.primaryHost)){
throw new URIError('The provided URI "' + storageHost.primaryHost + '" is invalid.');
}
storageHost.secondaryHost = _.isString(host) ? undefined : host.secondaryHost;
if (storageHost.secondaryHost && !check.isURL(storageHost.secondaryHost)){
throw new URIError('The provided URI "' + storageHost.secondaryHost + '" is invalid.');
}
if (!storageHost.primaryHost && !storageHost.secondaryHost) {
throw new ArgumentNullError('host', SR.STORAGE_HOST_LOCATION_REQUIRED);
}
}
return true;
};
/**
* Checks if the given value is a valid UUID or not.
*
* @param {string|object} uuid The uuid to validate.
* @return {boolean}
*/
exports.isValidUuid = function(uuid, callback) {
var validUuidRegex = /^[a-zA-Z0-9]{8}\-[a-zA-Z0-9]{4}\-[a-zA-Z0-9]{4}\-[a-zA-Z0-9]{4}\-[a-zA-Z0-9]{12}$/;
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
if (!validUuidRegex.test(uuid)) {
return fail(new SyntaxError('The value is not a valid UUID format.'));
}
callback();
return true;
};
/**
* Creates a anonymous function that check if a given key is base 64 encoded.
*
* @param {string} key The key to validate.
* @return {function}
*/
exports.isBase64Encoded = function (key) {
var isValidBase64String = key.match(/^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/);
if (isValidBase64String) {
return true;
} else {
throw new SyntaxError('The provided account key ' + key + ' is not a valid base64 string.');
}
};
/**
* Validates a function.
*
* @param {object} function The function to validate.
* @return {function}
*/
exports.isValidFunction = function (functionObject, functionName) {
if (!functionObject) {
throw new ArgumentNullError('functionObject', functionName + ' must be specified.');
}
if(!_.isFunction(functionObject)){
throw new TypeError(functionName + ' specified should be a function.');
}
return true;
};
var getNameError = function(name, typeName) {
// checks if name is null, undefined or empty
if (azureutil.stringIsEmpty(name)) {
return new ArgumentNullError('name', util.format('%s name must be a non empty string.', typeName));
}
// check if name is between 3 and 63 characters
if (name.length < 3 || name.length > 63) {
return new ArgumentError('name', util.format('%s name must be between 3 and 63 characters long.', typeName));
}
// check if name follows naming rules
if (name.match(/^([a-z0-9]+(-[a-z0-9]+)*)$/) === null) {
return new SyntaxError(util.format('%s name format is incorrect.', typeName));
}
return null;
};
/**
* Validates a container name.
*
* @param {string} containerName The container name.
*/
exports.containerNameIsValid = function (containerName, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
var nameError = getNameError(containerName, 'Container');
if (!nameError || containerName.match(/^(\$root|\$logs)$/)) {
callback();
return true;
} else {
return fail(nameError);
}
};
/**
* Validates a blob name.
*
* @param {string} containerName The container name.
* @param {string} blobname The blob name.
*/
exports.blobNameIsValid = function (containerName, blobName, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
if (!blobName) {
return fail(new ArgumentNullError('blobName', 'Blob name is not specified.'));
}
if (containerName === '$root' && blobName.indexOf('/') !== -1) {
return fail(new SyntaxError('Blob name format is incorrect.'));
}
callback();
return true;
};
/**
* Validates a blob tier name.
*
* @param {string} blobTier The blob tier name.
*/
exports.blobTierNameIsValid = function (blobTier, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
if (!blobTier) {
return fail(new ArgumentNullError('blobTier', 'Blob tier is not specified.'));
}
if (!_.chain(_.union(
_.values(BlobUtilities.BlobTier.PremiumPageBlobTier),
_.values(BlobUtilities.BlobTier.StandardBlobTier)
))
.map(function (val) { return val.toString().toUpperCase(); })
.contains(blobTier.toString().toUpperCase())
.value()) {
return fail(new SyntaxError('Blob tier is incorrect. Refer to BlobUtilities.BlobTier for possible values.'));
}
callback();
return true;
};
/**
* Validates a share name.
*
* @param {string} shareName The share name.
*/
exports.shareNameIsValid = function (shareName, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
var nameError = getNameError(shareName, 'Share');
if (!nameError) {
callback();
return true;
} else {
return fail(nameError);
}
};
/**
* Validates a queue name.
*
* @param {string} queueName The queue name.
*/
exports.queueNameIsValid = function (queueName, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
var nameError = getNameError(queueName, 'Queue');
if (!nameError) {
callback();
return true;
} else {
return fail(nameError);
}
};
/**
* Validates a table name.
*
* @param {string} table The table name.
*/
exports.tableNameIsValid = function (table, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
if (azureutil.stringIsEmpty(table)) {
return fail(new ArgumentNullError('table', 'Table name must be a non empty string.'));
}
if (table.length < 3 || table.length > 63) {
return fail(new ArgumentError('table', 'Table name must be between 3 and 63 characters long.'));
}
if(table.toLowerCase() === 'tables') {
return fail(new RangeError('Table name cannot be \'Tables\'.'));
}
if (table.match(/^([A-Za-z][A-Za-z0-9]{2,62})$/) !== null || table === '$MetricsCapacityBlob' || table.match(/^(\$Metrics(HourPrimary|MinutePrimary|HourSecondary|MinuteSecondary)?(Transactions)(Blob|Queue|Table|File))$/) !== null)
{
callback();
return true;
} else {
return fail(new SyntaxError('Table name format is incorrect.'));
}
};
/**
* Validates an HTML File object.
*
* @param {File} browserFile The HTML File object.
*/
exports.browserFileIsValid = function (browserFile, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
// IE doesn't support File.constructor.name
if (!azureutil.isBrowser() ||
!browserFile ||
!browserFile.constructor ||
(!azureutil.isIE() && !browserFile.constructor.name) ||
(!azureutil.isIE() && browserFile.constructor.name !== 'File' && browserFile.constructor.name !== 'Blob') ||
!azureutil.objectIsInt(browserFile.size)) {
return fail(new ArgumentError('type', 'Invalid HTML File object.'));
} else {
callback();
return true;
}
};
/**
* Validates page ranges.
*
* @param {int} rangeStart The range starting position.
* @param {int} rangeEnd The range ending position.
* @param {int} writeBlockSizeInBytes The block size.
*/
exports.pageRangesAreValid = function (rangeStart, rangeEnd, writeBlockSizeInBytes, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
if (rangeStart % 512 !== 0) {
return fail(new RangeError('Start byte offset must be a multiple of 512.'));
}
var size = null;
if (!azureutil.objectIsNull(rangeEnd)) {
if ((rangeEnd + 1) % 512 !== 0) {
return fail(new RangeError('End byte offset must be a multiple of 512 minus 1.'));
}
size = (rangeEnd - rangeStart) + 1;
if (size > writeBlockSizeInBytes) {
return fail(new RangeError('Page blob size cannot be larger than ' + writeBlockSizeInBytes + ' bytes.'));
}
}
callback();
return true;
};
/**
* Validates a blob type.
*
* @param {string} type The type name.
*/
exports.blobTypeIsValid = function (type, callback) {
var getEnumValues = function (obj) {
var values = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
values.push(obj[prop]);
}
}
return values;
};
return this.isValidEnumValue(type, getEnumValues(blobConstants.BlobTypes), callback);
};
/**
* Validates share ACL type.
*
* @param {string} type The type name.
*/
exports.shareACLIsValid = function (type, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
if (type != FileUtilities.SharePublicAccessType.OFF) {
fail(new ArgumentError('type', 'The access type is not supported.'));
}
callback();
return true;
};
/**
* Validates share quota value.
*
* @param {int} type The quota value.
*/
exports.shareQuotaIsValid = function (quota, callback) {
var fail;
initCallback(callback, function (f, cb) {
fail = f;
callback = cb;
});
if (quota && quota <= 0) {
fail(new RangeError('The share quota value, in GB, must be greater than 0.'));
}
callback();
return true;
};
// common functions for validating arguments
function throwMissingArgument(name, func) {
throw new ArgumentNullError(name, 'Required argument ' + name + ' for function ' + func + ' is not defined');
}
function ArgumentValidator(functionName) {
this.func = functionName;
}
_.extend(ArgumentValidator.prototype, {
string: function (val, name) {
this.exists(val, name);
if (typeof val !== 'string') {
throw new TypeError('Parameter ' + name + ' for function ' + this.func + ' should be a non-empty string');
}
},
stringAllowEmpty: function (val, name) {
if (typeof val !== 'string') {
throw new TypeError('Parameter ' + name + ' for function ' + this.func + ' should be a string');
}
},
object: function (val, name) {
this.exists(val, name);
if (typeof val !== 'object') {
throw new TypeError('Parameter ' + name + ' for function ' + this.func + ' should be an object');
}
},
exists: function (val, name) {
if (!val) {
throwMissingArgument(name, this.func);
}
},
function: function (val, name) {
this.exists(val, name);
if (typeof val !== 'function') {
throw new TypeError('Parameter ' + name + ' for function ' + this.func + ' should be a function');
}
},
value: function (val, name) {
if (!val && val !== 0) {
throwMissingArgument(name, this.func);
}
},
nonEmptyArray: function (val, name) {
if (!val || val.length === 0) {
throw new TypeError('Required array argument ' + name + ' for function ' + this.func + ' is either not defined or empty');
}
},
callback: function (val) {
this.exists(val, 'callback');
this.function(val, 'callback');
},
test: function (predicate, message) {
if (!predicate()) {
throw new Error(message + ' in function ' + this.func);
}
},
tableNameIsValid: exports.tableNameIsValid,
browserFileIsValid: exports.browserFileIsValid,
containerNameIsValid: exports.containerNameIsValid,
shareNameIsValid: exports.shareNameIsValid,
blobNameIsValid: exports.blobNameIsValid,
blobTierNameIsValid: exports.blobTierNameIsValid,
pageRangesAreValid: exports.pageRangesAreValid,
queueNameIsValid: exports.queueNameIsValid,
blobTypeIsValid: exports.blobTypeIsValid,
shareACLIsValid: exports.shareACLIsValid,
shareQuotaIsValid: exports.shareQuotaIsValid,
isValidEnumValue: exports.isValidEnumValue
});
function validateArgs(functionName, validationRules) {
var validator = new ArgumentValidator(functionName);
validationRules(validator);
}
exports.ArgumentValidator = ArgumentValidator;
exports.validateArgs = validateArgs;