Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #11 from andrerod/connectionStrings

Connection strings support
  • Loading branch information...
commit 08cb69400f128cb16055ca5a54550e3d57de53b8 2 parents a6830e1 + 3a30749
@andrerod andrerod authored
Showing with 1,958 additions and 275 deletions.
  1. +1 −1  examples/blobuploader/server.js
  2. +1 −1  examples/blog/blog.js
  3. +1 −1  examples/blog/public/stylesheets/style.css
  4. +3 −2 examples/geophoto/package.json
  5. +2 −2 examples/geophoto/services/pushpinService.js
  6. +3 −3 examples/geophoto/views/layout.ejs
  7. +2 −2 examples/myevents/server.js
  8. +1 −1  examples/tasklist/server.js
  9. +5 −0 lib/azure.js
  10. +10 −10 lib/services/blob/blobservice.js
  11. +8 −48 lib/services/core/servicebusserviceclient.js
  12. +137 −0 lib/services/core/servicebussettings.js
  13. +8 −19 lib/services/core/serviceclient.js
  14. +2 −10 lib/services/core/servicemanagementclient.js
  15. +120 −0 lib/services/core/servicemanagementsettings.js
  16. +238 −0 lib/services/core/servicesettings.js
  17. +100 −53 lib/services/core/storageserviceclient.js
  18. +300 −0 lib/services/core/storageservicesettings.js
  19. +9 −9 lib/services/queue/queueservice.js
  20. +42 −19 lib/services/serviceBus/servicebusservice.js
  21. +6 −4 lib/services/serviceBus/wrap.js
  22. +12 −29 lib/services/serviceBus/wrapservice.js
  23. +3 −4 lib/services/serviceBus/wraptokenmanager.js
  24. +2 −2 lib/services/serviceManagement/servicemanagementservice.js
  25. +2 −2 lib/services/table/batchserviceclient.js
  26. +11 −10 lib/services/table/tableservice.js
  27. +55 −0 lib/util/constants.js
  28. +38 −0 lib/util/util.js
  29. +50 −0 lib/util/validate.js
  30. +2 −1  package.json
  31. +13 −15 test/azure-tests.js
  32. +21 −14 test/services/blob/blobservice-tests.js
  33. +86 −0 test/services/core/servicebussettings-tests.js
  34. +94 −0 test/services/core/servicemanagementsettings-tests.js
  35. +59 −0 test/services/core/servicesettings-tests.js
  36. +310 −0 test/services/core/storageservicesettings-tests.js
  37. +16 −2 test/services/queue/queueservice-tests.js
  38. +24 −6 test/services/serviceBus/servicebusservice-tests.js
  39. +3 −1 test/services/table/tableservice-batch-tests.js
  40. +94 −4 test/services/table/tableservice-tests.js
  41. +5 −0 test/testlist.txt
  42. +19 −0 test/util/util-tests.js
  43. +40 −0 test/util/validate-tests.js
View
2  examples/blobuploader/server.js
@@ -31,7 +31,7 @@ var helpers = require('./helpers.js');
var app = module.exports = express.createServer();
// Global request options, set the retryPolicy
-var blobClient = azure.createBlobService(azure.ServiceClient.DEVSTORE_STORAGE_ACCOUNT, azure.ServiceClient.DEVSTORE_STORAGE_ACCESS_KEY, azure.ServiceClient.DEVSTORE_BLOB_HOST).withFilter(new azure.ExponentialRetryPolicyFilter());
+var blobClient = azure.createBlobService('UseDevelopmentStorage=true').withFilter(new azure.ExponentialRetryPolicyFilter());
var containerName = 'webpi';
//Configuration
View
2  examples/blog/blog.js
@@ -33,7 +33,7 @@ var tableName = 'posts';
var partition = 'part1';
Blog = function () {
- this.tableClient = azure.createTableService(ServiceClient.DEVSTORE_STORAGE_ACCOUNT, ServiceClient.DEVSTORE_STORAGE_ACCESS_KEY, ServiceClient.DEVSTORE_TABLE_HOST);
+ this.tableClient = azure.createTableService('UseDevelopmentStorage=true');
};
Blog.prototype.findAll = function (callback) {
View
2  examples/blog/public/stylesheets/style.css
@@ -1,7 +1,7 @@
body {
font-family: Tahoma, "Helvetica Neue", Arial, Helvetica, sans-serif;
font-size: 13px;
- text-stroke: 1px rgba(255,255,255,0.10);
+ text-stroke: 1px rgba(255,255,255,0.1);
color: #555;
background-color: #f3f4f5;
}
View
5 examples/geophoto/package.json
@@ -2,11 +2,12 @@
"name": "geophoto"
, "version": "0.0.1"
, "dependencies": {
- "azure": ">= 0.5.1"
- , "express": "2.5.1"
+ "express": "2.5.1"
, "ejs": ">= 0.0.1"
, "socket.io": "0.8.7"
, "node-uuid": ">= 1.3.3"
, "nconf": ">= 0.5.1"
+ , "uglify-js": ">= 1.3.3"
+ , "azure": ">= 0.5.1"
}
}
View
4 examples/geophoto/services/pushpinService.js
@@ -19,8 +19,8 @@ if (!fs.existsSync) {
}
var azure;
-if (fs.existsSync('./../../lib/azure.js')) {
- azure = require('./../../lib/azure');
+if (fs.existsSync('./../../../lib/azure.js')) {
+ azure = require('./../../../lib/azure');
} else {
azure = require('azure');
}
View
6 examples/geophoto/views/layout.ejs
@@ -14,11 +14,11 @@
<script type="text/javascript" src="socket.io/socket.io.js"></script>
<script type="text/javascript">
- var bingMapsCredentials = '<%= bingMapsCredentials %>';
+ var bingMapsCredentials = '<% if (locals.bingMapsCredentials) { %><%= bingMapsCredentials %><% } %>';
var initialPushpins = [
- <% pushpins.forEach(function(pushpin) { %>
+ <% if (locals.pushpins) { pushpins.forEach(function(pushpin) { %>
{ title: '<%= pushpin.title %>', description: '<%= pushpin.description %>', latitude: '<%= pushpin.latitude %>', longitude: '<%= pushpin.longitude %>', RowKey: '<%= pushpin.RowKey %>', PartitionKey: '<%= pushpin.PartitionKey %>', imageUrl: '<%= pushpin.imageUrl %>' },
- <% }) %>
+ <% }) } %>
];
</script>
View
4 examples/myevents/server.js
@@ -11,8 +11,8 @@ var express = require('express')
var app = module.exports = express.createServer();
-var tableClient = azure.createTableService(ServiceClient.DEVSTORE_STORAGE_ACCOUNT, ServiceClient.DEVSTORE_STORAGE_ACCESS_KEY, ServiceClient.DEVSTORE_TABLE_HOST);
-var blobClient = azure.createBlobService(azure.ServiceClient.DEVSTORE_STORAGE_ACCOUNT, azure.ServiceClient.DEVSTORE_STORAGE_ACCESS_KEY, azure.ServiceClient.DEVSTORE_BLOB_HOST).withFilter(new azure.ExponentialRetryPolicyFilter());
+var tableClient = azure.createTableService('UseDevelopmentStorage=true');
+var blobClient = azure.createBlobService('UseDevelopmentStorage=true').withFilter(new azure.ExponentialRetryPolicyFilter());
// Create table and blob
tableClient.createTableIfNotExists('events', function(error) {
View
2  examples/tasklist/server.js
@@ -32,7 +32,7 @@ var Home = require('./home');
var ServiceClient = azure.ServiceClient;
var app = module.exports = express.createServer();
-var client = azure.createTableService(ServiceClient.DEVSTORE_STORAGE_ACCOUNT, ServiceClient.DEVSTORE_STORAGE_ACCESS_KEY, ServiceClient.DEVSTORE_TABLE_HOST);
+var client = azure.createTableService('UseDevelopmentStorage=true');
// table creation
client.createTableIfNotExists("tasks", function (res, created) {
View
5 lib/azure.js
@@ -150,6 +150,11 @@ exports.SharedKeyLiteTable = require('./services/table/sharedkeylitetable');
exports.ISO8061Date = require('./util/iso8061date');
exports.Logger = require('./diagnostics/logger');
exports.ConnectionStringParser = require('./services/core/connectionstringparser');
+exports.ServiceSettings = require('./services/core/servicesettings');
+exports.StorageServiceSettings = require('./services/core/storageservicesettings');
+exports.ServiceBusSettings = require('./services/core/servicebussettings');
+exports.ServiceManagementSettings = require('./services/core/servicemanagementsettings');
+exports.Validate = require('./util/validate');
/*
* Convenience functions.
View
20 lib/services/blob/blobservice.js
@@ -16,6 +16,7 @@
// Module dependencies.
var fs = require('fs');
var qs = require('qs');
+var url = require('url');
var path = require('path');
var util = require('util');
var crypto = require('crypto');
@@ -76,15 +77,14 @@ BlobService.incorrectEndByteOffsetErr = 'End byte offset must be a modulus of 51
* @param {object} [authenticationProvider] The authentication provider.
*/
function BlobService(storageAccountOrConnectionString, storageAccessKey, host, authenticationProvider) {
- if (!host) {
- if (ServiceClient.isEmulated()) {
- host = ServiceClient.DEVSTORE_BLOB_HOST;
- } else {
- host = ServiceClient.CLOUD_BLOB_HOST;
- }
- }
+ var storageServiceSettings = StorageServiceClient.getStorageSettings(storageAccountOrConnectionString, storageAccessKey, host);
- BlobService.super_.call(this, storageAccountOrConnectionString, storageAccessKey, host, authenticationProvider);
+ BlobService.super_.call(this,
+ storageServiceSettings._name,
+ storageServiceSettings._key,
+ storageServiceSettings._blobEndpointUri,
+ storageServiceSettings._usePathStyleUri,
+ authenticationProvider);
if (!this.authenticationProvider) {
this.authenticationProvider = new SharedKey(this.storageAccount, this.storageAccessKey, this.usePathStyleUri);
@@ -2177,7 +2177,7 @@ BlobService.prototype.generateSharedAccessSignature = function (container, blob,
var resourceName = createResourceName(container, blob);
var signedQueryString = this.sharedAccessSignatureCredentials.generateSignedQueryString(resourceName, {}, resourceType, sharedAccessPolicy);
- var baseUrl = this.protocol + this._getHostname() + ':' + this.port;
+ var baseUrl = this.protocol + this.host + ':' + this.port;
var path = this._getPath('/' + resourceName);
return {
@@ -2204,7 +2204,7 @@ BlobService.prototype.getBlobUrl = function (container, blob) {
var resourceName = createResourceName(container, blob);
- var baseUrl = this.protocol + this._getHostname() + ':' + this.port;
+ var baseUrl = this.protocol + this.host + ':' + this.port;
var path = this._getPath('/' + resourceName);
return {
View
56 lib/services/core/servicebusserviceclient.js
@@ -33,49 +33,18 @@ exports = module.exports = ServiceBusServiceClient;
* Creates a new ServiceBusServiceClient object.
*
* @constructor
+* @param {string} accessKey The password.
+* @param {string} issuer The issuer.
* @param {string} host The host for the service.
-* @param {string} [namespace] The service bus namespace.
-* @param {string} [accessKey] The password.
-* @param {string} [issuer] The issuer.
-* @param {string} [acsNamespace] The acs namespace. Usually the same as the sb namespace with "-sb" suffix.
+* @param {string} acsHost The acs host. Usually the same as the sb namespace with "-sb" suffix.
* @param {object} [authenticationProvider] The authentication provider.
*/
-function ServiceBusServiceClient(host, namespace, accessKey, issuer, acsNamespace, authenticationProvider) {
- this.namespace = namespace;
- if (!this.namespace) {
- this.namespace = process.env[ServiceClient.EnvironmentVariables.AZURE_SERVICEBUS_NAMESPACE];
- }
-
- this.acsNamespace = acsNamespace;
- if (!this.acsNamespace) {
- this.acsNamespace = process.env[ServiceClient.EnvironmentVariables.AZURE_WRAP_NAMESPACE];
-
- if (!this.acsNamespace) {
- this.acsNamespace = this.namespace + ServiceClient.DEFAULT_WRAP_NAMESPACE_SUFFIX;
- }
- }
-
- this.issuer = issuer;
- if (!this.issuer) {
- this.issuer = process.env[ServiceClient.EnvironmentVariables.AZURE_SERVICEBUS_ISSUER];
-
- if (!this.issuer) {
- this.issuer = ServiceClient.DEFAULT_SERVICEBUS_ISSUER;
- }
- }
-
- this.accessKey = accessKey;
- if (!this.accessKey) {
- this.accessKey = process.env[ServiceClient.EnvironmentVariables.AZURE_SERVICEBUS_ACCESS_KEY];
- }
-
+function ServiceBusServiceClient(accessKey, issuer, host, acsHost, authenticationProvider) {
ServiceBusServiceClient.super_.call(this, host, authenticationProvider);
- this.protocol = 'https://';
- this.port = 443;
-
+ this.authenticationProvider = authenticationProvider;
if (!this.authenticationProvider) {
- this.authenticationProvider = new Wrap(this.acsNamespace, this.issuer, this.accessKey);
+ this.authenticationProvider = new Wrap(acsHost, issuer, accessKey);
}
}
@@ -100,7 +69,7 @@ ServiceBusServiceClient.prototype._buildRequestOptions = function (webResource,
}
webResource.addOptionalHeader(HeaderConstants.ACCEPT_CHARSET_HEADER, 'UTF-8');
- webResource.addOptionalHeader(HeaderConstants.HOST_HEADER, this._getHostname() + ':' + this.port);
+ webResource.addOptionalHeader(HeaderConstants.HOST_HEADER, this.host + ':' + this.port);
// Sets the request url in the web resource.
this._setRequestUrl(webResource);
@@ -121,7 +90,7 @@ ServiceBusServiceClient.prototype._buildRequestOptions = function (webResource,
requestOptions = {
url: url.format({
protocol: self._isHttps() ? 'https:' : 'http:',
- hostname: self._getHostname(),
+ hostname: self.host,
port: self.port,
pathname: webResource.path + webResource.getQueryString(true)
}),
@@ -152,13 +121,4 @@ ServiceBusServiceClient.prototype._getPath = function (path) {
}
return path;
-};
-
-/**
-* Retrives the hostname.
-*
-* @return {string} The hostname.
-*/
-ServiceBusServiceClient.prototype._getHostname = function () {
- return this.namespace + '.' + this.host;
};
View
137 lib/services/core/servicebussettings.js
@@ -0,0 +1,137 @@
+/**
+* Copyright (c) Microsoft. 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 url = require('url');
+
+var util = require('../../util/util');
+
+var ConnectionStringParser = require('./connectionstringparser');
+var ServiceSettings = require('./servicesettings');
+var Constants = require('../../util/constants');
+var ConnectionStringKeys = Constants.ConnectionStringKeys;
+var Validate = require('../../util/validate');
+
+exports = module.exports = ServiceBusSettings;
+
+var serviceBusEndpointSetting = ServiceSettings.settingWithFunc(ConnectionStringKeys.SERVICE_BUS_ENDPOINT_NAME, Validate.isValidUri);
+var wrapEndpointSetting = ServiceSettings.settingWithFunc(ConnectionStringKeys.WRAP_ENDPOINT_NAME, Validate.isValidUri);
+var wrapNameSetting = ServiceSettings.setting(ConnectionStringKeys.SHARED_SECRET_ISSUER_NAME);
+var wrapPasswordSetting = ServiceSettings.setting(ConnectionStringKeys.SHARED_SECRET_VALUE_NAME);
+
+var validKeys = [
+ ConnectionStringKeys.SERVICE_BUS_ENDPOINT_NAME,
+ ConnectionStringKeys.WRAP_ENDPOINT_NAME,
+ ConnectionStringKeys.SHARED_SECRET_ISSUER_NAME,
+ ConnectionStringKeys.SHARED_SECRET_VALUE_NAME
+];
+
+/**
+* Creates new service bus settings instance.
+*
+* @param {string} serviceBusEndpointUri The Service Bus endpoint uri.
+* @param {string} wrapEndpointUri The Service Bus endpoint uri.
+* @param {string} namespace The service namespace.
+* @param {string} wrapName The wrap name.
+* @param {string} wrapPassword The wrap password.
+*/
+function ServiceBusSettings(serviceBusEndpointUri, wrapEndpointUri, namespace, wrapName, wrapPassword) {
+ this._namespace = namespace;
+ this._serviceBusEndpointUri = serviceBusEndpointUri;
+
+ if (wrapEndpointUri) {
+ this._wrapEndpointUri = wrapEndpointUri;
+ } else {
+ this._wrapEndpointUri = 'https://' + namespace + '-sb.accesscontrol.windows.net/WRAPv0.9';
+ }
+
+ this._wrapName = wrapName;
+ this._wrapPassword = wrapPassword;
+}
+
+/**
+* Creates a ServiceBusSettings object from a set of settings.
+*
+* @param {object} settings The settings object.
+*
+* @return {ServiceBusSettings}
+*/
+ServiceBusSettings.createFromSettings = function (settings) {
+ var matchedSpecs = ServiceSettings.matchedSpecification(
+ settings,
+ ServiceSettings.allRequired(
+ serviceBusEndpointSetting,
+ wrapNameSetting,
+ wrapPasswordSetting
+ ),
+ ServiceSettings.optional(wrapEndpointSetting)
+ );
+
+ if (matchedSpecs) {
+ var endpoint = util.tryGetValueInsensitive(
+ ConnectionStringKeys.SERVICE_BUS_ENDPOINT_NAME,
+ settings
+ );
+
+ var wrapEndpoint = util.tryGetValueInsensitive(
+ ConnectionStringKeys.WRAP_ENDPOINT_NAME,
+ settings
+ );
+
+ // Parse the namespace part from the URI
+ var parsedUrl = url.parse(endpoint);
+ var namespace = parsedUrl.host.split('.')[0];
+
+ var issuerName = util.tryGetValueInsensitive(
+ ConnectionStringKeys.SHARED_SECRET_ISSUER_NAME,
+ settings
+ );
+
+ var issuerValue = util.tryGetValueInsensitive(
+ ConnectionStringKeys.SHARED_SECRET_VALUE_NAME,
+ settings
+ );
+
+ return new ServiceBusSettings(
+ endpoint,
+ wrapEndpoint,
+ namespace,
+ issuerName,
+ issuerValue
+ );
+ }
+
+ ServiceSettings.noMatchSettings(settings);
+};
+
+/**
+* Creates a ServiceBusSettings object from the given connection string.
+*
+* @param {string} connectionString The storage settings connection string.
+*
+* @return {ServiceBusSettings}
+*/
+ServiceBusSettings.createFromConnectionString = function (connectionString) {
+ var tokenizedSettings = ServiceSettings.parseAndValidateKeys(connectionString, validKeys);
+ try {
+ return ServiceBusSettings.createFromSettings(tokenizedSettings);
+ } catch (e) {
+ if (e instanceof ServiceSettings.NoMatchError) {
+ // Replace no match settings exception by no match connection string one.
+ ServiceSettings.noMatchConnectionString(connectionString);
+ } else {
+ throw e;
+ }
+ }
+};
View
27 lib/services/core/serviceclient.js
@@ -53,8 +53,8 @@ ServiceClient.EnvironmentVariables = {
/**
* Default credentials.
*/
-ServiceClient.DEVSTORE_STORAGE_ACCOUNT = 'devstoreaccount1';
-ServiceClient.DEVSTORE_STORAGE_ACCESS_KEY = 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==';
+ServiceClient.DEVSTORE_STORAGE_ACCOUNT = Constants.DEV_STORE_NAME;
+ServiceClient.DEVSTORE_STORAGE_ACCESS_KEY = Constants.DEV_STORE_KEY;
/**
* Development ServiceClient URLs.
@@ -106,14 +106,13 @@ function ServiceClient(host, authenticationProvider) {
this._initDefaultFilter();
if (host) {
- var parsedHost = this._parseHost(host);
+ var parsedHost = ServiceClient._parseHost(host);
this.host = parsedHost.hostname;
this.port = parsedHost.port;
if (!this.protocol) {
this.protocol = parsedHost.protocol + '//';
}
- }
- else if (!this.protocol) {
+ } else if (!this.protocol) {
this.protocol = ServiceClient.DEFAULT_PROTOCOL;
}
@@ -380,7 +379,7 @@ ServiceClient.prototype._setRequestOptionsProxy = function (requestOptions) {
* @param {string} host The full host to be parsed.
* @return {object} THe parsed host as returned by the method "url.parse".
*/
-ServiceClient.prototype._parseHost = function (host) {
+ServiceClient._parseHost = function (host) {
var fullHost = host;
if (fullHost.indexOf(Constants.HTTP) === -1) {
fullHost = ServiceClient.DEFAULT_PROTOCOL + fullHost;
@@ -515,7 +514,7 @@ ServiceClient.prototype._setRequestUrl = function (webResource) {
var queryString = webResource.getQueryString(true);
// Build the full request url
- webResource.requestUrl = this.protocol + this._getHostname() + ":" + this.port + webResource.path + queryString;
+ webResource.requestUrl = this.protocol + this.host + ":" + this.port + webResource.path + queryString;
};
/**
@@ -564,24 +563,14 @@ ServiceClient.prototype.parseMetadataHeaders = function (headers) {
};
/**
-* Determines if the service client is running on an emulated environment.
-* This will be considered to be the case if the used url matches the address for the devstore
-* (See DEVSTORE_BLOB_HOST, DEVSTORE_TABLE_HOST and DEVSTORE_QUEUE_HOST) or if the environment variable
-* "EMULATED" is set.
+* Gets the value of the environment variable for is emulated.
*
* @param {string} host The used host.
* @return {bool} True if the service client is running on an emulated environment; false otherwise.
*/
ServiceClient.isEmulated = function (host) {
- if (host === ServiceClient.DEVSTORE_BLOB_HOST ||
- host === ServiceClient.DEVSTORE_TABLE_HOST ||
- host === ServiceClient.DEVSTORE_QUEUE_HOST) {
-
- return true;
- }
-
return (!azureutil.objectIsNull(process.env[ServiceClient.EnvironmentVariables.EMULATED]) &&
- process.env[ServiceClient.EnvironmentVariables.EMULATED] !== 'false');
+ process.env[ServiceClient.EnvironmentVariables.EMULATED] !== 'false');
};
// Other functions
View
12 lib/services/core/servicemanagementclient.js
@@ -190,7 +190,7 @@ ServiceManagementClient.prototype._buildRequestOptions = function(webResource, o
var requestOptions = {
url: url.format({
protocol: self._isHttps() ? 'https' : 'http',
- hostname: self._getHostname(),
+ hostname: self.host,
port: self.port,
pathname: webResource.path + webResource.getQueryString(true)
}),
@@ -302,12 +302,4 @@ ServiceManagementClient.prototype._setAgent = function(self, reqopts, isHTTPS) {
} else if (isHTTPS) {
reqopts.agent = new https.Agent(reqopts);
}
-};
-
-/**
-* Get service host name
-*/
-ServiceManagementClient.prototype._getHostname = function () {
- return this.host;
-};
-
+};
View
120 lib/services/core/servicemanagementsettings.js
@@ -0,0 +1,120 @@
+/**
+* Copyright (c) Microsoft. 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 url = require('url');
+
+var util = require('../../util/util');
+
+var ConnectionStringParser = require('./connectionstringparser');
+var ServiceSettings = require('./servicesettings');
+var Constants = require('../../util/constants');
+var ConnectionStringKeys = Constants.ConnectionStringKeys;
+var Validate = require('../../util/validate');
+
+exports = module.exports = ServiceManagementSettings;
+
+var endpointSetting = ServiceSettings.settingWithFunc(
+ ConnectionStringKeys.SERVICE_MANAGEMENT_ENDPOINT_NAME,
+ Validate.isValidUri
+);
+
+var certificatePathSetting = ServiceSettings.setting(ConnectionStringKeys.CERTIFICATE_PATH_NAME);
+var subscriptionIdSetting = ServiceSettings.setting(ConnectionStringKeys.SUBSCRIPTION_ID_NAME);
+
+var validKeys = [
+ ConnectionStringKeys.SUBSCRIPTION_ID_NAME,
+ ConnectionStringKeys.CERTIFICATE_PATH_NAME,
+ ConnectionStringKeys.SERVICE_MANAGEMENT_ENDPOINT_NAME
+];
+
+/**
+* Creates new service management settings instance.
+*
+* @param {string} subscriptionId The user provided subscription id.
+* @param {string} endpointUri The service management endpoint uri.
+* @param {string} certificatePath The management certificate path.
+*/
+function ServiceManagementSettings(subscriptionId, endpointUri, certificatePath) {
+ this._subscriptionId = subscriptionId;
+ this._endpointUri = endpointUri;
+ this._certificatePath = certificatePath;
+}
+
+/**
+* Creates a ServiceBusSettings object from a set of settings.
+*
+* @param {object} settings The settings object.
+*
+* @return {ServiceManagementSettings}
+*/
+ServiceManagementSettings.createFromSettings = function (settings) {
+ var matchedSpecs = ServiceSettings.matchedSpecification(
+ settings,
+ ServiceSettings.allRequired(
+ subscriptionIdSetting,
+ certificatePathSetting
+ ),
+ ServiceSettings.optional(
+ endpointSetting
+ )
+ );
+
+ if (matchedSpecs) {
+ var endpointUri = util.tryGetValueInsensitive(
+ ConnectionStringKeys.SERVICE_MANAGEMENT_ENDPOINT_NAME,
+ settings,
+ Constants.SERVICE_MANAGEMENT_URL
+ );
+
+ var subscriptionId = util.tryGetValueInsensitive(
+ ConnectionStringKeys.SUBSCRIPTION_ID_NAME,
+ settings
+ );
+
+ var certificatePath = util.tryGetValueInsensitive(
+ ConnectionStringKeys.CERTIFICATE_PATH_NAME,
+ settings
+ );
+
+ return new ServiceManagementSettings(
+ subscriptionId,
+ endpointUri,
+ certificatePath
+ );
+ }
+
+ ServiceSettings.noMatchSettings(settings);
+};
+
+/**
+* Creates a ServiceManagementSettings object from the given connection string.
+*
+* @param {string} connectionString The storage settings connection string.
+*
+* @return {ServiceManagementSettings}
+*/
+ServiceManagementSettings.createFromConnectionString = function (connectionString) {
+ var tokenizedSettings = ServiceSettings.parseAndValidateKeys(connectionString, validKeys);
+ try {
+ return ServiceManagementSettings.createFromSettings(tokenizedSettings);
+ } catch (e) {
+ if (e instanceof ServiceSettings.NoMatchError) {
+ // Replace no match settings exception by no match connection string one.
+ ServiceSettings.noMatchConnectionString(connectionString);
+ } else {
+ throw e;
+ }
+ }
+}
View
238 lib/services/core/servicesettings.js
@@ -0,0 +1,238 @@
+/**
+* Copyright (c) Microsoft. 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 ut = require('util');
+var util = require('../../util/util');
+var _ = require('underscore');
+
+var ConnectionStringParser = require('./connectionstringparser');
+
+exports = module.exports;
+
+var NoMatchError = function (msg, constr) {
+ Error.captureStackTrace(this, constr || this);
+ this.message = msg || 'Error';
+}
+
+ut.inherits(NoMatchError, Error);
+NoMatchError.prototype.name = 'NoMatchError';
+
+exports.NoMatchError = NoMatchError;
+
+/**
+* Throws an exception if the connection string format does not match any of the
+* available formats.
+*
+* @param {string} connectionString The invalid formatted connection string.
+*
+* @return none
+*/
+exports.noMatchConnectionString = function (connectionString) {
+ throw new NoMatchError('The provided connection string "' + connectionString + '" does not have complete configuration settings.');
+};
+
+/**
+* Throws an exception if the settings dont match any of the
+* available formats.
+*
+* @param {object} settings The invalid settings.
+*
+* @return none
+*/
+exports.noMatchSettings = function (settings) {
+ throw new NoMatchError('The provided settings ' + JSON.stringify(settings) + ' are not complete.');
+};
+
+/**
+* Parses the connection string and then validate that the parsed keys belong to
+* the validSettingKeys
+*
+* @param {string} connectionString The user provided connection string.
+* @param {array} validKeys The valid keys.
+* @return {array} The tokenized connection string keys.
+*/
+exports.parseAndValidateKeys = function (connectionString, validKeys) {
+ var tokenizedSettings = ConnectionStringParser.parse(connectionString);
+
+ // Assure that all given keys are valid.
+ for (var key in tokenizedSettings) {
+ if (tokenizedSettings.hasOwnProperty(key)) {
+ if (!util.inArrayInsensitive(key, validKeys)) {
+ throw new Error('Invalid connection string setting key "' + key + '"');
+ }
+ }
+ }
+
+ return tokenizedSettings;
+};
+
+/**
+* Creates an anonymous function that acts as predicate to perform a validation.
+*
+* @param array {requirements} The array of conditions to satisfy.
+* @param boolean {isRequired} Either these conditions are all required or all
+* optional.
+* @param boolean {atLeastOne} Indicates that at least one requirement must
+* succeed.
+*
+* @return {function}
+*/
+exports.getValidator = function (requirements, isRequired, atLeastOne) {
+ return function (userSettings) {
+ var oneFound = false;
+ var result = { };
+
+ for (var key in userSettings) {
+ if (userSettings.hasOwnProperty(key)) {
+ result[key.toLowerCase()] = userSettings[key];
+ }
+ }
+
+ for (var requirement in requirements) {
+ if (requirements.hasOwnProperty(requirement)) {
+ var settingName = requirements[requirement]['SettingName'].toLowerCase();
+
+ // Check if the setting name exists in the provided user settings.
+ if (result[settingName]) {
+ // Check if the provided user setting value is valid.
+ var validationFunc = requirements[requirement]['SettingConstraint'];
+ var isValid = validationFunc(result[settingName]);
+
+ if (isValid) {
+ // Remove the setting as indicator for successful validation.
+ delete result[settingName];
+ oneFound = true;
+ }
+ } else if (isRequired) {
+ // If required then fail because the setting does not exist
+ return null;
+ }
+ }
+ }
+
+ if (atLeastOne) {
+ // At least one requirement must succeed, otherwise fail.
+ return oneFound ? result : null;
+ } else {
+ return result;
+ }
+ };
+};
+
+/**
+* Creates a setting value condition that validates it is one of the
+* passed valid values.
+*
+* @param {string} name The setting key name.
+*
+* @return {array}
+*/
+exports.setting = function (name) {
+ var validValues = Array.prototype.slice.call(arguments, 1, arguments.length);
+
+ var predicate = function (settingValue) {
+ var validValuesString = JSON.stringify(validValues);
+ if (validValues.length === 0) {
+ // No restrictions, succeed.
+ return true;
+ }
+
+ // Check to find if the settingValue is valid or not.
+ for (var index = 0; index < validValues.length; index++) {
+ if (settingValue.toString() == validValues[index].toString()) {
+ // SettingValue is found in valid values set, succeed.
+ return true;
+ }
+ }
+
+ // settingValue is missing in valid values set, fail.
+ throw new Error('The provided config value ' + settingValue + ' does not belong to the valid values subset:\n' + validValuesString);
+ }
+
+ return exports.settingWithFunc(name, predicate);
+};
+
+/**
+* Creates an "at lease one" predicate for the provided list of requirements.
+*
+* @return callable
+*/
+exports.atLeastOne = function () {
+ var allSettings = arguments;
+ return exports.getValidator(allSettings, false, true);
+};
+
+/**
+* Creates an optional predicate for the provided list of requirements.
+*
+* @return {function}
+*/
+exports.optional = function () {
+ var optionalSettings = arguments;
+ return exports.getValidator(optionalSettings, false, false);
+};
+
+/**
+* Creates an required predicate for the provided list of requirements.
+*
+* @return {function}
+*/
+exports.allRequired = function () {
+ var requiredSettings = arguments;
+ return exports.getValidator(requiredSettings, true, false);
+};
+
+/**
+* Creates a setting value condition using the passed predicate.
+*
+* @param {string} name The setting key name.
+* @param {function} predicate The setting value predicate.
+*
+* @return {array}
+*/
+exports.settingWithFunc = function (name, predicate) {
+ var requirement = {};
+ requirement['SettingName'] = name;
+ requirement['SettingConstraint'] = predicate;
+
+ return requirement;
+};
+
+
+/**
+* Tests to see if a given list of settings matches a set of filters exactly.
+*
+* @param array $settings The settings to check.
+*
+* @return boolean If any filter returns null, false. If there are any settings
+* left over after all filters are processed, false. Otherwise true.
+*/
+exports.matchedSpecification = function (settings) {
+ var constraints = Array.prototype.slice.call(arguments, 1, arguments.length);
+
+ for (var constraint in constraints) {
+ if (constraints.hasOwnProperty(constraint)) {
+ var remainingSettings = constraints[constraint](settings);
+
+ if (!remainingSettings) {
+ return false;
+ } else {
+ settings = remainingSettings;
+ }
+ }
+ }
+
+ return util.objectKeysLength(settings) === 0;
+};
View
153 lib/services/core/storageserviceclient.js
@@ -23,7 +23,7 @@ var xml2js = require('xml2js');
var azureutil = require('../../util/util');
-var ConnectionStringParser = require('../core/connectionstringparser');
+var StorageServiceSettings = require('../core/storageservicesettings');
var ServiceClient = require('./serviceclient');
var Constants = require('../../util/constants');
@@ -36,52 +36,113 @@ var Logger = require('../../diagnostics/logger');
exports = module.exports = StorageServiceClient;
// Validation error messages
-StorageServiceClient.incorrectStorageAccountErr = 'Account name must be a non empty string. Set AZURE_STORAGE_ACCOUNT';
-StorageServiceClient.incorrectStorageAccessKeyErr = 'AccessKey must be a non empty string. Set AZURE_STORAGE_ACCESS_KEY';
+StorageServiceClient.incorrectStorageAccountErr = 'You must supply an account name or use the environment variable AZURE_STORAGE_ACCOUNT if you are not running in the emulator.';
+StorageServiceClient.incorrectStorageAccessKeyErr = 'You must supply an account key or use the environment variable AZURE_STORAGE_ACCESS_KEY if you are not running in the emulator.';
/**
* Creates a new ServiceClient object.
*
* @constructor
-* @param {string} storageAccountOrConnectionString The storage account or connection string.
-* @param {string} storageAccessKey The storage access key.
-* @param {string} host The host for the service.
-* @param {object} authenticationProvider The authentication provider object (e.g. sharedkey / sharedkeytable / sharedaccesssignature).
+* @param {string} storageAccount The storage account.
+* @param {string} storageAccessKey The storage access key.
+* @param {string} host The host for the service.
+* @param {bool} usePathStyleUri Boolean value indicating wether to use path style uris.
+* @param {object} authenticationProvider The authentication provider object (e.g. sharedkey / sharedkeytable / sharedaccesssignature).
*/
-function StorageServiceClient(storageAccountOrConnectionString, storageAccessKey, host, authenticationProvider) {
- var storageAccount = storageAccountOrConnectionString;
+function StorageServiceClient(storageAccount, storageAccessKey, host, usePathStyleUri, authenticationProvider) {
+ this._setAccountCredentials(storageAccount, storageAccessKey);
+ this.apiVersion = HeaderConstants.TARGET_STORAGE_VERSION;
+ this.usePathStyleUri = usePathStyleUri;
+
+ StorageServiceClient.super_.call(this, host, authenticationProvider);
+
+ this._initDefaultFilter();
+}
+
+util.inherits(StorageServiceClient, ServiceClient);
+
+/**
+* Gets the storage settings.
+*
+* @param {string} [storageAccountOrConnectionString] The storage account or the connection string.
+* @param {string} [storageAccessKey] The storage access key.
+* @param {string} [host] The host address.
+*
+* @return {StorageServiceSettings}
+*/
+StorageServiceClient.getStorageSettings = function (storageAccountOrConnectionString, storageAccessKey, host) {
+ var storageServiceSettings;
if (storageAccountOrConnectionString && !storageAccessKey) {
- // If namespaceOrConnectionString was passed and no accessKey was passed, assume connection string
- var connectionString = ConnectionStringParser.parse(storageAccountOrConnectionString);
- if (connectionString['accountname']) {
- storageAccount = connectionString['accountname'];
- }
+ // If storageAccountOrConnectionString was passed and no accessKey was passed, assume connection string
+ storageServiceSettings = StorageServiceSettings.createFromConnectionString(storageAccountOrConnectionString);
+ } else if (!(storageAccountOrConnectionString && storageAccessKey) && ServiceClient.isEmulated()) {
+ // Dev storage scenario
+ storageServiceSettings = StorageServiceSettings.getDevelopmentStorageAccountSettings();
+ } else {
+ // Explicit or environment variable credentials scenario
+ storageServiceSettings = StorageServiceClient._getStorageSettingsExplicitOrEnvironment(
+ storageAccountOrConnectionString,
+ storageAccessKey,
+ host);
+ }
- if (connectionString['accountkey']) {
- storageAccessKey = connectionString['accountkey'];
- }
+ return storageServiceSettings;
+};
- if (connectionString['defaultendpointsprotocol']) {
- if (connectionString['defaultendpointsprotocol'] !== 'http' &&
- connectionString['defaultendpointsprotocol'] !== 'https') {
- throw new Error('Invalid protocol ' + connectionString['defaultendpointsprotocol']);
- }
+/**
+* Gets the storage settings from the parameters or environment variables.
+*
+* @param {string} [storageAccount] The storage account or the connection string.
+* @param {string} [storageAccessKey] The storage access key.
+* @param {string} [host] The host address.
+*
+* @return {StorageServiceSettings}
+*/
+StorageServiceClient._getStorageSettingsExplicitOrEnvironment = function (storageAccount, storageAccessKey, host) {
+ var usePathStyleUri = false;
- this.protocol = connectionString['defaultendpointsprotocol'] + '://';
- }
+ if (!storageAccount) {
+ storageAccount = process.env[ServiceClient.EnvironmentVariables.AZURE_STORAGE_ACCOUNT];
}
- this._setAccountCredentials(storageAccount, storageAccessKey);
- this.apiVersion = HeaderConstants.TARGET_STORAGE_VERSION;
- this.usePathStyleUri = ServiceClient.isEmulated(host);
+ if (!storageAccessKey) {
+ storageAccessKey = process.env[ServiceClient.EnvironmentVariables.AZURE_STORAGE_ACCESS_KEY];
+ }
- StorageServiceClient.super_.call(this, host, authenticationProvider);
+ // Default endpoints
+ var blobendpoint = url.format({ protocol: 'http:', hostname: storageAccount + '.' + ServiceClient.CLOUD_BLOB_HOST });
+ var tableendpoint = url.format({ protocol: 'http:', hostname: storageAccount + '.' + ServiceClient.CLOUD_TABLE_HOST });
+ var queueendpoint = url.format({ protocol: 'http:', hostname: storageAccount + '.' + ServiceClient.CLOUD_QUEUE_HOST });
- this._initDefaultFilter();
-}
+ if (host) {
+ var parsedHost = ServiceClient._parseHost(host);
-util.inherits(StorageServiceClient, ServiceClient);
+ var parsedHostUrl = url.format(parsedHost);
+ if (parsedHostUrl === url.format(ServiceClient._parseHost(ServiceClient.DEVSTORE_BLOB_HOST)) ||
+ parsedHostUrl === url.format(ServiceClient._parseHost(ServiceClient.DEVSTORE_TABLE_HOST)) ||
+ parsedHostUrl === url.format(ServiceClient._parseHost(ServiceClient.DEVSTORE_QUEUE_HOST))) {
+ usePathStyleUri = true;
+ }
+
+ blobendpoint = url.format(parsedHostUrl);
+ tableendpoint = url.format(parsedHostUrl);
+ queueendpoint = url.format(parsedHostUrl);
+ }
+
+ var settings = {
+ accountname: storageAccount,
+ accountkey: storageAccessKey,
+ blobendpoint: blobendpoint,
+ tableendpoint: tableendpoint,
+ queueendpoint: queueendpoint
+ };
+
+ var storageServiceSettings = StorageServiceSettings.createFromSettings(settings);
+ storageServiceSettings._usePathStyleUri = usePathStyleUri;
+
+ return storageServiceSettings;
+};
/**
* Builds the request options to be passed to the http.request method.
@@ -109,7 +170,7 @@ StorageServiceClient.prototype._buildRequestOptions = function (webResource, opt
webResource.addOptionalHeader(HeaderConstants.ACCEPT_HEADER, 'application/atom+xml,application/xml');
webResource.addOptionalHeader(HeaderConstants.ACCEPT_CHARSET_HEADER, 'UTF-8');
- webResource.addOptionalHeader(HeaderConstants.HOST_HEADER, this._getHostname() + ':' + this.port);
+ webResource.addOptionalHeader(HeaderConstants.HOST_HEADER, this.host + ':' + this.port);
if (options) {
if (options.timeoutIntervalInMs) {
@@ -136,7 +197,7 @@ StorageServiceClient.prototype._buildRequestOptions = function (webResource, opt
requestOptions = {
url: url.format({
protocol: self._isHttps() ? 'https:' : 'http:',
- hostname: self._getHostname(),
+ hostname: self.host,
port: self.port,
pathname: webResource.path + webResource.getQueryString(true)
}),
@@ -176,20 +237,6 @@ StorageServiceClient.prototype._getPath = function (path) {
};
/**
-* Retrives the hostname taking into consideration the usePathStyleUri flag which indicates whether the account
-* should be a prefix for it or not.
-*
-* @return {string} The hostname.
-*/
-StorageServiceClient.prototype._getHostname = function () {
- if (this.usePathStyleUri) {
- return this.host;
- }
-
- return this.storageAccount + '.' + this.host;
-};
-
-/**
* Sets the account credentials taking into consideration the isEmulated value and the environment variables:
* AZURE_STORAGE_ACCOUNT and AZURE_STORAGE_ACCESS_KEY.
*
@@ -199,18 +246,18 @@ StorageServiceClient.prototype._getHostname = function () {
*/
StorageServiceClient.prototype._setAccountCredentials = function (storageAccount, storageAccessKey) {
if (azureutil.objectIsNull(storageAccount)) {
- if (ServiceClient.isEmulated()) {
- storageAccount = ServiceClient.DEVSTORE_STORAGE_ACCOUNT;
- } else {
+ if (process.env[ServiceClient.EnvironmentVariables.AZURE_STORAGE_ACCOUNT]) {
storageAccount = process.env[ServiceClient.EnvironmentVariables.AZURE_STORAGE_ACCOUNT];
+ } else if (ServiceClient.isEmulated()) {
+ storageAccount = ServiceClient.DEVSTORE_STORAGE_ACCOUNT;
}
}
if (azureutil.objectIsNull(storageAccessKey)) {
- if (ServiceClient.isEmulated()) {
- storageAccessKey = ServiceClient.DEVSTORE_STORAGE_ACCESS_KEY;
- } else {
+ if (process.env[ServiceClient.EnvironmentVariables.AZURE_STORAGE_ACCESS_KEY]) {
storageAccessKey = process.env[ServiceClient.EnvironmentVariables.AZURE_STORAGE_ACCESS_KEY];
+ } else if (ServiceClient.isEmulated()) {
+ storageAccessKey = ServiceClient.DEVSTORE_STORAGE_ACCESS_KEY;
}
}
View
300 lib/services/core/storageservicesettings.js
@@ -0,0 +1,300 @@
+/**
+* Copyright (c) Microsoft. 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 url = require('url');
+
+var util = require('../../util/util');
+
+var ConnectionStringParser = require('./connectionstringparser');
+var ServiceSettings = require('./servicesettings');
+var Constants = require('../../util/constants');
+var ConnectionStringKeys = Constants.ConnectionStringKeys;
+var Validate = require('../../util/validate');
+
+exports = module.exports = StorageServiceSettings;
+
+var devStoreAccount = null;
+var useDevelopmentStorageSetting = ServiceSettings.setting(ConnectionStringKeys.USE_DEVELOPMENT_STORAGE_NAME, true);
+var developmentStorageProxyUriSetting = ServiceSettings.settingWithFunc(ConnectionStringKeys.DEVELOPMENT_STORAGE_PROXY_URI_NAME, Validate.isValidUri);
+var defaultEndpointsProtocolSetting = ServiceSettings.setting(ConnectionStringKeys.DEFAULT_ENDPOINTS_PROTOCOL_NAME, 'http', 'https');
+var accountNameSetting = ServiceSettings.setting(ConnectionStringKeys.ACCOUNT_NAME_NAME);
+var accountKeySetting = ServiceSettings.settingWithFunc(ConnectionStringKeys.ACCOUNT_KEY_NAME, Validate.isBase64Encoded);
+
+var blobEndpointSetting = ServiceSettings.settingWithFunc(
+ ConnectionStringKeys.BLOB_ENDPOINT_NAME,
+ Validate.isValidUri
+);
+
+var queueEndpointSetting = ServiceSettings.settingWithFunc(
+ ConnectionStringKeys.QUEUE_ENDPOINT_NAME,
+ Validate.isValidUri
+);
+
+var tableEndpointSetting = ServiceSettings.settingWithFunc(
+ ConnectionStringKeys.TABLE_ENDPOINT_NAME,
+ Validate.isValidUri
+);
+
+var validKeys = [
+ ConnectionStringKeys.USE_DEVELOPMENT_STORAGE_NAME,
+ ConnectionStringKeys.DEVELOPMENT_STORAGE_PROXY_URI_NAME,
+ ConnectionStringKeys.DEFAULT_ENDPOINTS_PROTOCOL_NAME,
+ ConnectionStringKeys.ACCOUNT_NAME_NAME,
+ ConnectionStringKeys.ACCOUNT_KEY_NAME,
+ ConnectionStringKeys.BLOB_ENDPOINT_NAME,
+ ConnectionStringKeys.QUEUE_ENDPOINT_NAME,
+ ConnectionStringKeys.TABLE_ENDPOINT_NAME
+];
+
+/**
+* Creates new storage service settings instance.
+*
+* @param {string} name The storage service name.
+* @param {string} key The storage service key.
+* @param {string} blobEndpointUri The storage service blob endpoint.
+* @param {string} queueEndpointUri The storage service queue endpoint.
+* @param {string} tableEndpointUri The storage service table endpoint.
+* @param {bool} usePathStyleUri Boolean value indicating wether to use path style uri or not.
+*/
+function StorageServiceSettings(name, key, blobEndpointUri, queueEndpointUri, tableEndpointUri, usePathStyleUri) {
+ this._name = name;
+ this._key = key;
+ this._blobEndpointUri = blobEndpointUri;
+ this._queueEndpointUri = queueEndpointUri;
+ this._tableEndpointUri = tableEndpointUri;
+
+ if (usePathStyleUri) {
+ this._usePathStyleUri = usePathStyleUri;
+ } else {
+ this._usePathStyleUri = false;
+ }
+}
+
+/**
+* Returns a StorageServiceSettings with development storage credentials using
+* the specified proxy Uri.
+*
+* @param {string} proxyUri The proxy endpoint to use.
+*
+* @return {StorageServiceSettings}
+*/
+StorageServiceSettings._getDevelopmentStorageAccount = function (proxyUri) {
+ if (!proxyUri) {
+ return StorageServiceSettings.getDevelopmentStorageAccountSettings();
+ }
+
+ var parsedUri = url.parse(proxyUri);
+ var scheme = parsedUri.protocol;
+ var host = parsedUri.host;
+ var prefix = scheme + '//' + host;
+
+ return new StorageServiceSettings(
+ Constants.DEV_STORE_NAME,
+ Constants.DEV_STORE_KEY,
+ prefix + ':10000',
+ prefix + ':10001',
+ prefix + ':10002',
+ true
+ );
+};
+
+/**
+* Gets a StorageServiceSettings object that references the development storage
+* account.
+*
+* @return {StorageServiceSettings}
+*/
+StorageServiceSettings.getDevelopmentStorageAccountSettings = function () {
+ if (!devStoreAccount) {
+ devStoreAccount = StorageServiceSettings._getDevelopmentStorageAccount(Constants.DEV_STORE_URI);
+ }
+
+ return devStoreAccount;
+};
+
+/**
+* Gets the default service endpoint using the specified protocol and account
+* name.
+*
+* @param {array} settings The service settings.
+* @param {string} dns The service DNS.
+*
+* @return {string}
+*/
+StorageServiceSettings._getDefaultServiceEndpoint = function (settings, dns) {
+ var scheme = util.tryGetValueInsensitive(
+ ConnectionStringKeys.DEFAULT_ENDPOINTS_PROTOCOL_NAME,
+ settings
+ );
+
+ var accountName = util.tryGetValueInsensitive(
+ ConnectionStringKeys.ACCOUNT_NAME_NAME,
+ settings
+ );
+
+ var port = '80';
+ if (scheme.toLowerCase() === 'https') {
+ port = '443';
+ }
+
+ return url.format({ protocol: scheme, hostname: accountName + '.' + dns, port: port });
+};
+
+
+/**
+* Creates StorageServiceSettings object given endpoints uri.
+*
+* @param {array} settings The service settings.
+* @param {string} blobEndpointUri The blob endpoint uri.
+* @param {string} queueEndpointUri The queue endpoint uri.
+* @param {string} tableEndpointUri The table endpoint uri.
+*
+* @return {StorageServiceSettings}
+*/
+StorageServiceSettings._createStorageServiceSettings = function (settings, blobEndpointUri, queueEndpointUri, tableEndpointUri) {
+ var blobEndpointUri = util.tryGetValueInsensitive(
+ ConnectionStringKeys.BLOB_ENDPOINT_NAME,
+ settings,
+ blobEndpointUri
+ );
+
+ var queueEndpointUri = util.tryGetValueInsensitive(
+ ConnectionStringKeys.QUEUE_ENDPOINT_NAME,
+ settings,
+ queueEndpointUri
+ );
+
+ var tableEndpointUri = util.tryGetValueInsensitive(
+ ConnectionStringKeys.TABLE_ENDPOINT_NAME,
+ settings,
+ tableEndpointUri
+ );
+
+ var accountName = util.tryGetValueInsensitive(
+ ConnectionStringKeys.ACCOUNT_NAME_NAME,
+ settings
+ );
+
+ var accountKey = util.tryGetValueInsensitive(
+ ConnectionStringKeys.ACCOUNT_KEY_NAME,
+ settings
+ );
+
+ return new StorageServiceSettings(
+ accountName,
+ accountKey,
+ blobEndpointUri,
+ queueEndpointUri,
+ tableEndpointUri
+ );
+};
+
+/**
+* Creates a ServiceBusSettings object from a set of settings.
+*
+* @param {object} settings The settings object.
+*
+* @return {StorageServiceSettings}
+*/
+StorageServiceSettings.createFromSettings = function (settings) {
+ // Devstore case
+ var matchedSpecs = ServiceSettings.matchedSpecification(
+ settings,
+ ServiceSettings.allRequired(useDevelopmentStorageSetting),
+ ServiceSettings.optional(developmentStorageProxyUriSetting)
+ );
+
+ if (matchedSpecs) {
+ var proxyUri = util.tryGetValueInsensitive(
+ ConnectionStringKeys.DEVELOPMENT_STORAGE_PROXY_URI_NAME,
+ settings
+ );
+
+ return this._getDevelopmentStorageAccount(proxyUri);
+ }
+
+ // Automatic case
+ matchedSpecs = ServiceSettings.matchedSpecification(
+ settings,
+ ServiceSettings.allRequired(
+ defaultEndpointsProtocolSetting,
+ accountNameSetting,
+ accountKeySetting
+ ),
+ ServiceSettings.optional(
+ blobEndpointSetting,
+ queueEndpointSetting,
+ tableEndpointSetting
+ )
+ );
+
+ if (matchedSpecs) {
+ return this._createStorageServiceSettings(
+ settings,
+ this._getDefaultServiceEndpoint(
+ settings,
+ ConnectionStringKeys.BLOB_BASE_DNS_NAME
+ ),
+ this._getDefaultServiceEndpoint(
+ settings,
+ ConnectionStringKeys.QUEUE_BASE_DNS_NAME
+ ),
+ this._getDefaultServiceEndpoint(
+ settings,
+ ConnectionStringKeys.TABLE_BASE_DNS_NAME
+ ));
+ }
+
+ // Explicit case
+ matchedSpecs = ServiceSettings.matchedSpecification(
+ settings,
+ ServiceSettings.atLeastOne(
+ blobEndpointSetting,
+ queueEndpointSetting,
+ tableEndpointSetting
+ ),
+ ServiceSettings.allRequired(
+ accountNameSetting,
+ accountKeySetting
+ )
+ );
+
+ if (matchedSpecs) {
+ return this._createStorageServiceSettings(settings);
+ }
+
+ ServiceSettings.noMatchSettings(settings);
+};
+
+/**
+* Creates a StorageServiceSettings object from the given connection string.
+*
+* @param {string} connectionString The storage settings connection string.
+*
+* @return {StorageServiceSettings}
+*/
+StorageServiceSettings.createFromConnectionString = function (connectionString) {
+ var tokenizedSettings = ServiceSettings.parseAndValidateKeys(connectionString, validKeys);
+ try {
+ return StorageServiceSettings.createFromSettings(tokenizedSettings);
+ } catch (e) {
+ if (e instanceof ServiceSettings.NoMatchError) {
+ // Replace no match settings exception by no match connection string one.
+ ServiceSettings.noMatchConnectionString(connectionString);
+ } else {
+ throw e;
+ }
+ }
+};
View
18 lib/services/queue/queueservice.js
@@ -15,6 +15,7 @@
// Module dependencies.
var util = require('util');
+var url = require('url');
var azureutil = require('../../util/util');
@@ -50,15 +51,14 @@ exports = module.exports = QueueService;
* @param {object} [authenticationProvider] The authentication provider.
*/
function QueueService(storageAccountOrConnectionString, storageAccessKey, host, authenticationProvider) {
- if (!host) {
- if (ServiceClient.isEmulated()) {
- host = ServiceClient.DEVSTORE_QUEUE_HOST;
- } else {
- host = ServiceClient.CLOUD_QUEUE_HOST;
- }
- }
-
- QueueService.super_.call(this, storageAccountOrConnectionString, storageAccessKey, host, authenticationProvider);
+ var storageServiceSettings = StorageServiceClient.getStorageSettings(storageAccountOrConnectionString, storageAccessKey, host);
+
+ QueueService.super_.call(this,
+ storageServiceSettings._name,
+ storageServiceSettings._key,
+ storageServiceSettings._queueEndpointUri,
+ storageServiceSettings._usePathStyleUri,
+ authenticationProvider);
if (!this.authenticationProvider) {
this.authenticationProvider = new SharedKey(this.storageAccount, this.storageAccessKey, this.usePathStyleUri);
View
61 lib/services/serviceBus/servicebusservice.js
@@ -29,7 +29,7 @@ var QueryStringConstants = Constants.QueryStringConstants;
var HttpConstants = Constants.HttpConstants;
var HeaderConstants = Constants.HeaderConstants;
-var ConnectionStringParser = require('../core/connectionstringparser');
+var ServiceBusSettings = require('../core/servicebussettings');
var QueueResult = require('./models/queueresult');
var QueueMessageResult = require('./models/queuemessageresult');
@@ -54,35 +54,58 @@ exports = module.exports = ServiceBusService;
* @param {object} [authenticationProvider] The authentication provider.
*/
function ServiceBusService(namespaceOrConnectionString, accessKey, issuer, acsNamespace, host, authenticationProvider) {
- if (!host) {
- host = ServiceClient.CLOUD_SERVICEBUS_HOST;
- }
-
- var namespace = namespaceOrConnectionString;
+ var serviceBusSettings;
if (namespaceOrConnectionString && !accessKey) {
// If namespaceOrConnectionString was passed and no accessKey was passed, assume connection string
- var connectionString = ConnectionStringParser.parse(namespaceOrConnectionString);
- if (connectionString['endpoint']) {
- var endpoint = url.parse(connectionString['endpoint']);
- namespace = endpoint.host.split('.')[0];
- host = endpoint.host.substring(namespace.length + 1);
+ serviceBusSettings = ServiceBusSettings.createFromConnectionString(namespaceOrConnectionString);
+ } else {
+ if (!namespaceOrConnectionString) {
+ namespaceOrConnectionString = process.env[ServiceClient.EnvironmentVariables.AZURE_SERVICEBUS_NAMESPACE];
}
- if (connectionString['sharedsecretissuer']) {
- issuer = connectionString['sharedsecretissuer'];
+ if (!accessKey) {
+ accessKey = process.env[ServiceClient.EnvironmentVariables.AZURE_SERVICEBUS_ACCESS_KEY];
}
- if (connectionString['sharedsecretvalue']) {
- accessKey = connectionString['sharedsecretvalue'];
+ if (!issuer) {
+ issuer = process.env[ServiceClient.EnvironmentVariables.AZURE_SERVICEBUS_ISSUER];
+
+ if (!issuer) {
+ issuer = ServiceClient.DEFAULT_SERVICEBUS_ISSUER;
+ }
}
- if (connectionString['stsendpoint']) {
- var endpoint = url.parse(connectionString['stsendpoint']);
- acsNamespace = endpoint.host.split('.')[0];
+ if (!acsNamespace) {
+ acsNamespace = process.env[ServiceClient.EnvironmentVariables.AZURE_WRAP_NAMESPACE];
+
+ if (!acsNamespace) {
+ acsNamespace = namespaceOrConnectionString + ServiceClient.DEFAULT_WRAP_NAMESPACE_SUFFIX;
+ }
}
+
+ var endpoint = url.format({ protocol: 'https:', port: 443, hostname: namespaceOrConnectionString + '.' + ServiceClient.CLOUD_SERVICEBUS_HOST });
+ var stsendpoint = url.format({ protocol: 'https:', port: 443, hostname: acsNamespace + '.' + ServiceClient.CLOUD_ACCESS_CONTROL_HOST });
+
+ if (host) {
+ endpoint = url.format(ServiceClient._parseHost(host));
+ }
+
+ var settings = {
+ endpoint: endpoint,
+ sharedsecretissuer: issuer,
+ sharedsecretvalue: accessKey,
+ stsendpoint: stsendpoint
+ };
+
+ serviceBusSettings = ServiceBusSettings.createFromSettings(settings);
}
- ServiceBusService.super_.call(this, host, namespace, accessKey, issuer, acsNamespace, authenticationProvider);
+ ServiceBusService.super_.call(this,
+ serviceBusSettings._wrapPassword,
+ serviceBusSettings._wrapName,
+ serviceBusSettings._serviceBusEndpointUri,
+ serviceBusSettings._wrapEndpointUri,
+ authenticationProvider);
}
util.inherits(ServiceBusService, ServiceBusServiceClient);
View
10 lib/services/serviceBus/wrap.js
@@ -36,13 +36,15 @@ exports = module.exports = Wrap;
/**
* Creates a new Wrap object.
*
-* @param {string} acsNamespace The access control namespace.
+* @param {string} acsHost The access control host.
* @param {string} issuer The service bus issuer.
* @param {string} accessKey The service bus issuer password.
-* @param {string} host The host for the service.
*/
-function Wrap(acsNamespace, issuer, accessKey, host) {
- this.wrapTokenManager = new WrapTokenManager(acsNamespace, issuer, accessKey, host);
+function Wrap(acsHost, issuer, accessKey) {
+ this.acsHost = acsHost;
+ this.issuer = issuer;
+ this.accessKey = accessKey;
+ this.wrapTokenManager = new WrapTokenManager(acsHost, issuer, accessKey);
}
/**
View
41 lib/services/serviceBus/wrapservice.js
@@ -35,23 +35,18 @@ exports = module.exports = WrapService;
/**
* Creates a new WrapService object.
*
-* @param {string} acsNamespace The access control namespace.
-* @param {string} issuer The service bus issuer.
-* @param {string} accessKey The service bus issuer password.
-* @param {string} host The host for the service.
+* @param {string} acsHost The access control host.
+* @param {string} [issuer] The service bus issuer.
+* @param {string} [accessKey] The service bus issuer password.
*/
-function WrapService(acsNamespace, issuer, accessKey, host) {
- if (!host) {
- host = ServiceClient.CLOUD_ACCESS_CONTROL_HOST;
- }
-
- this.acsNamespace = acsNamespace;
- if (!this.acsNamespace) {
- this.acsNamespace = process.env[ServiceClient.EnvironmentVariables.AZURE_WRAP_NAMESPACE];
-
- if (!this.acsNamespace) {
- this.acsNamespace = process.env[ServiceClient.EnvironmentVariables.AZURE_SERVICEBUS_NAMESPACE] + ServiceClient.DEFAULT_WRAP_NAMESPACE_SUFFIX;
+function WrapService(acsHost, issuer, accessKey) {
+ if (!acsHost) {
+ var acsNamespace = process.env[ServiceClient.EnvironmentVariables.AZURE_WRAP_NAMESPACE];
+ if (!acsNamespace) {
+ acsNamespace = process.env[ServiceClient.EnvironmentVariables.AZURE_SERVICEBUS_NAMESPACE] + ServiceClient.DEFAULT_WRAP_NAMESPACE_SUFFIX;
}
+
+ acsHost = url.format({ protocol: 'https:', port: 443, hostname: acsNamespace + '.' + ServiceClient.CLOUD_ACCESS_CONTROL_HOST });
}
this.issuer = issuer;
@@ -68,10 +63,7 @@ function WrapService(acsNamespace, issuer, accessKey, host) {
this.accessKey = process.env[ServiceClient.EnvironmentVariables.AZURE_SERVICEBUS_ACCESS_KEY];
}
- WrapService.super_.call(this, host);
-
- this.protocol = 'https://';
- this.port = 443;
+ WrapService.super_.call(this, acsHost);
}
util.inherits(WrapService, ServiceClient);
@@ -129,7 +121,7 @@ WrapService.prototype._buildRequestOptions = function (webResource, options, cal
var requestOptions = {
url: url.format({
protocol: self._isHttps() ? 'https:' : 'http:',
- hostname: self._getHostname(),
+ hostname: self.host,
port: self.port,
pathname: webResource.path + webResource.getQueryString(true)
}),
@@ -159,15 +151,6 @@ WrapService.prototype._getPath = function (path) {
};
/**
-* Retrives the hostname.
-*
-* @return {string} The hostname.
-*/
-WrapService.prototype._getHostname = function () {
- return this.acsNamespace + '.' + this.host;
-};
-
-/**
* Validates a callback function.
*
* @param (function) callback The callback function.
View
7 lib/services/serviceBus/wraptokenmanager.js
@@ -35,15 +35,14 @@ var ONE_SECOND_MS = 1000;
/**
* Creates a new Wrap object.
*
-* @param {string} acsNamespace The access control namespace.
+* @param {string} acsHost The access control host.
* @param {string} issuer The service bus issuer.
* @param {string} accessKey The service bus issuer password.
-* @param {string} host The host for the service.
*/
-function WrapTokenManager(acsNamespace, issuer, accessKey, host) {
+function WrapTokenManager(acsHost, issuer, accessKey) {
this.activeTokens = { };
- this.wrapService = new WrapService(acsNamespace, issuer, accessKey, host);
+ this.wrapService = new WrapService(acsHost, issuer, accessKey);
}
WrapTokenManager.prototype.getAccessToken = function(scopeUri, callback) {
View
4 lib/services/serviceManagement/servicemanagementservice.js
@@ -31,7 +31,7 @@ exports = module.exports = ServiceManagementService;
* Creates a new ServiceManagementService object.
*
* @constructor
-* @param {string} subscriptionIdOrConnectionString The subscription ID for the account or the connectionString.
+* @param {string} subscriptionId The subscription ID for the account or the connectionString.
* @param {string} authentication The authentication object for the client.
* {
* keyfile: 'path to .pem',
@@ -46,7 +46,7 @@ exports = module.exports = ServiceManagementService;
* serializetype: 'XML'
* }
*/
-function ServiceManagementService(subscriptionIdOrConnectionString, authentication, hostOptions) {
+function ServiceManagementService(subscriptionId, authentication, hostOptions) {
if (typeof subscriptionId != 'string' || subscriptionId.length === 0) {
throw new Error('A subscriptionId or a connection string is required');
}
View
4 lib/services/table/batchserviceclient.js
@@ -39,8 +39,8 @@ BatchServiceClient.BATCH_CODE = -1;
*
* @constructor
*/
-function BatchServiceClient(host, storageAccount, storageAccessKey, authenticationProvider) {
- BatchServiceClient.super_.call(this, host, storageAccount, storageAccessKey, authenticationProvider);
+function BatchServiceClient(storageAccount, storageAccessKey, host, usePathStyleUri, authenticationProvider) {
+ BatchServiceClient.super_.call(this, storageAccount, storageAccessKey, host, usePathStyleUri, authenticationProvider);
this.operations = null;
}
View
21 lib/services/table/tableservice.js
@@ -15,11 +15,13 @@
// Module dependencies.
var util = require('util');
+var url = require('url');
var xmlbuilder = require('xmlbuilder');
var azureutil = require('../../util/util');
-var BatchServiceClient = require("./batchserviceclient");
+var StorageServiceClient = require('../core/storageserviceclient');
+var BatchServiceClient = require('./batchserviceclient');
var SharedKeyTable = require('./sharedkeytable');
var TableQuery = require('./tablequery');
var AtomHandler = require('../../util/atomhandler');
@@ -60,15 +62,14 @@ TableService.incorrectPartitionErr = 'PartitionKey and RowKey must be specified
* @param {object} [authenticationProvider] The authentication provider.
*/
function TableService(storageAccountOrConnectionString, storageAccessKey, host, authenticationProvider) {
- if (!host) {
- if (ServiceClient.isEmulated()) {
- host = ServiceClient.DEVSTORE_TABLE_HOST;
- } else {
- host = ServiceClient.CLOUD_TABLE_HOST;
- }
- }
-
- TableService.super_.call(this, storageAccountOrConnectionString, storageAccessKey, host, authenticationProvider);
+ var storageServiceSettings = StorageServiceClient.getStorageSettings(storageAccountOrConnectionString, storageAccessKey, host);
+
+ TableService.super_.call(this,
+ storageServiceSettings._name,
+ storageServiceSettings._key,
+ storageServiceSettings._tableEndpointUri,
+ storageServiceSettings._usePathStyleUri,
+ authenticationProvider);
if (!this.authenticationProvider) {
this.authenticationProvider = new SharedKeyTable(this.storageAccount, this.storageAccessKey, this.usePathStyleUri);
View
55 lib/util/constants.js
@@ -818,6 +818,38 @@ var Constants = {
ATOM_SUBSCRIPTION_DESCRIPTION_MARKER: 'SubscriptionDescription',
/**
+ * The development store URI.
+ *
+ * @const
+ * @type {string}
+ */
+ DEV_STORE_URI: 'http://127.0.0.1',
+
+ /**
+ * The development store account name.
+ *
+ * @const
+ * @type {string}
+ */
+ DEV_STORE_NAME: 'devstoreaccount1',
+
+ /**
+ * The development store account key.
+ *
+ * @const
+ * @type {string}
+ */
+ DEV_STORE_KEY: 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==',
+
+ /**
+ * The service management URI.
+ *
+ * @const
+ * @type {string}
+ */
+ SERVICE_MANAGEMENT_URL: 'https://management.core.windows.net',
+
+ /**
* Defines constants for use with blob operations.
*/
BlobConstants: {
@@ -2556,6 +2588,29 @@ var Constants = {
GeneralErrorCodeStrings: {
INVALID_CONTENT_TYPE: 'The response content type is invalid'
+ },
+
+ ConnectionStringKeys: {
+ USE_DEVELOPMENT_STORAGE_NAME: 'UseDevelopmentStorage',
+ DEVELOPMENT_STORAGE_PROXY_URI_NAME: 'DevelopmentStorageProxyUri',
+ DEFAULT_ENDPOINTS_PROTOCOL_NAME: 'DefaultEndpointsProtocol',
+ ACCOUNT_NAME_NAME: 'AccountName',
+ ACCOUNT_KEY_NAME: 'AccountKey',
+ BLOB_ENDPOINT_NAME: 'BlobEndpoint',
+ QUEUE_ENDPOINT_NAME: 'QueueEndpoint',
+ TABLE_ENDPOINT_NAME: 'TableEndpoint',
+ SHARED_ACCESS_SIGNATURE_NAME: 'SharedAccessSignature',
+ BLOB_BASE_DNS_NAME: 'blob.core.windows.net',
+ QUEUE_BASE_DNS_NAME: 'queue.core.windows.net',
+ TABLE_BASE_DNS_NAME: 'table.core.windows.net',
+ DEV_STORE_CONNECTION_STRING: 'BlobEndpoint=127.0.0.1:10000;QueueEndpoint=127.0.0.1:10001;TableEndpoint=127.0.0.1:10002;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==',
+ SUBSCRIPTION_ID_NAME: 'SubscriptionID',
+ CERTIFICATE_PATH_NAME: 'CertificatePath',
+ SERVICE_MANAGEMENT_ENDPOINT_NAME: 'ServiceManagementEndpoint',
+ SERVICE_BUS_ENDPOINT_NAME: 'Endpoint',
+ WRAP_ENDPOINT_NAME: 'StsEndpoint',
+ SHARED_SECRET_ISSUER_NAME: 'SharedSecretIssuer',
+ SHARED_SECRET_VALUE_NAME: 'SharedSecretValue'
}
};
View
38 lib/util/util.js
@@ -217,6 +217,44 @@ exports.merge = function () {
return _.extend.apply(this, arguments);
};
+/**
+* Checks if a value exists in an array. The comparison is done in a case
+* insensitive manner.
+*
+* @param {string} needle The searched value.
+* @param {array} haystack The array.
+*
+* @static
+*
+* @return {boolean}
+*/
+exports.inArrayInsensitive = function (needle, haystack) {
+ return _.contains(_.map(haystack, function (h) { return h.toLowerCase() }), needle.toLowerCase());
+};
+
+/**
+* Returns the specified value of the key passed from object and in case that
+* this key doesn't exist, the default value is returned. The key matching is
+* done in a case insensitive manner.
+*
+* @param {string} key The array key.
+* @param {object} haystack The object to be used.
+* @param {mix} default The value to return if $key is not found in $array.
+*
+* @static
+*
+* @return mix
+*/
+exports.tryGetValueInsensitive = function (key, haystack, defaultValue) {
+ for (var i in haystack) {
+ if (i.toString().toLowerCase() === key.toString().toLowerCase()) {
+ return haystack[i];
+ }
+ }
+
+ return defaultValue;
+};
+
exports.pathExistsSync = fs.existsSync
? fs.existsSync
: path.existsSync;
View
50 lib/util/validate.js
@@ -0,0 +1,50 @@
+/**
+* Copyright (c) Microsoft. 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 check = require('validator').check;
+
+exports = module.exports;
+
+/**
+* Creates a anonymous function that check if the given uri is valid or not.
+*
+* @param {string} uri The uri to validate.
+* @return {function}
+*/
+exports.isValidUri = function (uri) {
+ try {
+ // Check will throw if it is not valid.
+ check(uri).isUrl();
+ return true;
+ } catch (e) {
+ throw new Error('The provided URI "' + uri + '" is invalid.');
+ }
+};
+
+/**
+* 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 Error('The provided account key ' + key + ' is not a valid base64 string.');
+ }
+};
View
3  package.json
@@ -27,7 +27,8 @@
"colors": "0.x.x",
"eyes": "0.x.x",
"easy-table": "0.0.1",
- "request": ">= 2.9.203"
+ "request": ">= 2.9.203",
+ "validator": ">= 0.4.12"
},
"devDependencies": {
"mocha": "*",
View
28 test/azure-tests.js
@@ -24,14 +24,14 @@ var ServiceBusServiceClient = testutil.libRequire('services/core/servicebusservi
var ServiceClient = azure.ServiceClient;
var environmentAzureStorageAccount = 'myaccount';
-var environmentAzureStorageAccessKey = 'myaccountstoragekey';
+var environmentAzureStorageAccessKey = 'AhlzsbLRkjfwObuqff3xrhB2yWJNh1EMptmcmxFJ6fvPTVX3PZXwrG2YtYWf5DPMVgNsteKStM5iBLlknYFVoA==';
var environmentServiceBusNamespace = 'mynamespace';
var environmentServiceBusIssuer = 'myissuer';
-var environmentServiceBusAccessKey = 'myaccesskey';
+var environmentServiceBusAccessKey = 'AhlzsbLRkjfwObuqff3xrhB2yWJNh1EMptmcmxFJ6fvPTVX3PZXwrG2YtYWf5DPMVgNsteKStM5iBLlknYFVoA==';
var environmentWrapNamespace = 'mynamespace-sb';
var parameterAzureStorageAccount = 'storageAccount';
-var parameterAzureStorageAccessKey = 'storageAccesKey';
+var parameterAzureStorageAccessKey = 'AhlzsbLRkjfwObuqff3xrhB2yWJNh1EMptmcmxFJ6fvPTVX3PZXwrG2YtYWf5DPMVgNsteKStM5iBLlknYFVoA==';
var firstRun = true;
var originalAzureStorageAccount = null;
@@ -136,11 +136,10 @@ suite('azure', function () {
process.env[ServiceClient.EnvironmentVariables.AZURE_STORAGE_ACCESS_KEY] = environmentAzureStorageAccessKey;
var blobService1 = azure.createBlobService();
- assert.equal(blobService1.host, ServiceClient.CLOUD_BLOB_HOST);
+ assert.equal(blobService1.host, environmentAzureStorageAccount + '.' + ServiceClient.CLOUD_BLOB_HOST);
assert.equal(blobService1.usePathStyleUri, false);
process.env[ServiceClient.EnvironmentVariables.EMULATED] = true;
- assert.equal(azure.isEmulated(), true);
var blobService2 = azure.createBlobService();
assert.equal(blobService2.host, '127.0.0.1');
@@ -162,7 +161,7 @@ suite('azure', function () {
// Points to the live services
assert.equal(blobService.usePathStyleUri, false);
- assert.equal(blobService.host, ServiceClient.CLOUD_BLOB_HOST);
+ assert.equal(blobService.host, parameterAzureStorageAccount.toLowerCase() + '.' + ServiceClient.CLOUD_BLOB_HOST);
// And credentials are the ones passed
assert.equal(blobService.authenticationProvider.storageAccount, parameterAzureStorageAccount);
@@ -182,9 +181,9 @@ suite('azure', function () {
// Create blob client passing some credentials
var blobService = azure.createBlobService(parameterAzureStorageAccount, parameterAzureStorageAccessKey);
- // Points to the emulator
- assert.equal(blobService.usePathStyleUri, true);
- assert.equal(blobService.host + ':' + blobService.port, ServiceClient.DEVSTORE_BLOB_HOST);
+ // Points to the credentials
+ assert.equal(blobService.usePathStyleUri, false);
+ assert.equal(blobService.host + ':' + blobService.port, parameterAzureStorageAccount.toLowerCase() + '.' + ServiceClient.CLOUD_BLOB_HOST + ':80');
// But the used credentials are the ones passed because we were explicit
assert.equal(blobService.authenticationProvider.storageAccount, parameterAzureStorageAccount);
@@ -228,7 +227,7 @@ suite('azure', function () {
// Points to the live service
assert.equal(blobService.usePathStyleUri, false);
- assert.equal(blobService.host, ServiceClient.CLOUD_BLOB_HOST);
+ assert.equal(blobService.host, environmentAzureStorageAccount + '.' + ServiceClient.CLOUD_BLOB_HOST);
// and uses the environment variables
assert.equal(blobService.authenticationProvider.storageAccount, environmentAzureStorageAccount);
@@ -237,7 +236,7 @@ suite('azure', function () {
done();
});
- test('MissingServiceBusIssuerAndWrapNamespace', function (done) {
+test('MissingServiceBusIssuerAndWrapNamespace', function (done) {
delete process.env[ServiceClient.EnvironmentVariables.AZURE_WRAP_NAMESPACE];
delete process.env[ServiceClient.EnvironmentVariables.AZURE_SERVICEBUS_ISSUER];
@@ -248,12 +247,11 @@ suite('azure', function () {
var serviceBusService = azure.createServiceBusService();
// set correctly
- assert.equal(serviceBusService.namespace, environmentServiceBusNamespace);
- assert.equal(serviceBusService.accessKey, environmentServiceBusAccessKey);
+ assert.equal(serviceBusService.authenticationProvider.acsHost, 'https://' + environmentServiceBusNamespace + ServiceClient.DEFAULT_WRAP_NAMESPACE_SUFFIX + '.accesscontrol.windows.net:443');
+ assert.equal(serviceBusService.authenticationProvider.accessKey, environmentServiceBusAccessKey);
// defaulted correctly
- assert.equal(serviceBusService.acsNamespace, environmentServiceBusNamespace + ServiceClient.DEFAULT_WRAP_NAMESPACE_SUFFIX);
- assert.equal(serviceBusService.issuer, ServiceClient.DEFAULT_SERVICEBUS_ISSUER);
+ assert.equal(serviceBusService.authenticationProvider.issuer, ServiceClient.DEFAULT_SERVICEBUS_ISSUER);
done();
});
View
35 test/services/blob/blobservice-tests.js
@@ -1164,21 +1164,13 @@ suite('blobservice-tests', function () {
var containerName = testutil.generateId(containerNamesPrefix, containerNames, blobtestutil.isMocked);
var blobName = testutil.generateId(blobNamesPrefix, blobNames, blobtestutil.isMocked);
- var blobServiceassert = azure.createBlobService('storageAccount', 'storageAccessKey', 'host:80');
- blobServiceassert.usePathStyleUri = false;
+ var blobServiceassert = azure.createBlobService('storageAccount', 'storageAccessKey', 'host.com:80');
var urlParts = blobServiceassert.getBlobUrl(containerName);
- assert.equal(urlParts.url(), 'http://storageAccount.host:80/' + containerName);
+ assert.equal(urlParts.url(), 'http://host.com:80/' + containerName);
urlParts = blobServiceassert.getBlobUrl(containerName, blobName);
- assert.equal(urlParts.url(), 'http://storageAccount.host:80/' + containerName + '/' + blobName);
-
- blobServiceassert.usePathStyleUri = true;
- urlParts = blobServiceassert.getBlobUrl(containerName);
- assert.equal(urlParts.url(), 'http://host:80/storageAccount/' + containerName);
-
- urlParts = blobServiceassert.getBlobUrl(containerName, blobName);
- assert.equal(urlParts.url(), 'http://host:80/storageAccount/' + containerName + '/' + blobName);
+ assert.equal(urlParts.url(), 'http://host.com:80/' + containerName + '/' + blobName);
done();
});
@@ -1274,16 +1266,31 @@ suite('blobservice-tests', function () {
});
test('storageConnectionStrings', function (done) {
- var connectionString = 'DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=mykey';
+ var key = 'AhlzsbLRkjfwObuqff3xrhB2yWJNh1EMptmcmxFJ6fvPTVX3PZXwrG2YtYWf5DPMVgNsteKStM5iBLlknYFVoA==';
+ var connectionString = 'DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=' + key;
var blobService = azure.createBlobService(connectionString);
assert.equal(blobService.storageAccount, 'myaccount');
- assert.equal(blobService.storageAccessKey, 'mykey');
+ assert.equal(blobService.storageAccessKey, key);
assert.equal(blobService.protocol, 'https://');
- assert.equal(blobService.host, 'blob.core.windows.net');
+ assert.equal(blobService.host, 'myaccount.blob.core.windows.net');
+
+ done();
+ });
+
+ test('storageConnectionStringsDevStore', function (done) {
+ var connectionString = 'UseDevelopmentStorage=true';
+ var blobService = azure.createBlobService(connectionString);
+
+ assert.equal(blobService.storageAccount, ServiceClient.DEVSTORE_STORAGE_ACCOUNT);
+ assert.equal(blobService.storageAccessKey, ServiceClient.DEVSTORE_STORAGE_ACCESS_KEY);
+ assert.equal(blobService.protocol, 'http://');
+ assert.equal(blobService.host, '127.0.0.1');
+ assert.equal(blobService.port, '10000');
done();
});
+
});
function repeat(s, n) {
View
86 test/services/core/servicebussettings-tests.js
@@ -0,0 +1,86 @@
+/**
+* Copyright (c) Microsoft. 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 should = require('should');
+var url = require('url');
+
+var testutil = require('../../util/util');
+var azure = testutil.libRequire('azure');
+var Constants = azure.Constants;
+var ConnectionStringKeys = Constants.ConnectionStringKeys;
+var ServiceBusSettings = azure.ServiceBusSettings;
+
+suite('servicebussettings-tests', function () {
+ test('testCreateFromConnectionStringWithServiceBusAutomaticCase', function () {
+ // Setup
+ var expectedNamespace = 'mynamespace';
+ var expectedServiceBusEndpoint = 'https://' + expectedNamespace + '.servicebus.windows.net';
+ var expectedWrapName = 'myname';
+ var expectedWrapPassword = 'mypassword';
+ var expectedWrapEndpointUri = 'https://' + expectedNamespace + '-sb.accesscontrol.windows.net/WRAPv0.9';
+ var connectionString = 'Endpoint=' + expectedServiceBusEndpoint + ';SharedSecretIssuer=' + expectedWrapName + ';SharedSecretValue=' + expectedWrapPassword;
+
+ // Test
+ var actual = ServiceBusSettings.createFromConnectionString(connectionString);
+
+ // Assert
+ actual._namespace.should.equal(expectedNamespace);
+ actual._serviceBusEndpointUri.should.equal(expectedServiceBusEndpoint);
+ actual._wrapName.should.equal(expectedWrapName);
+ actual._wrapPassword.should.equal(expectedWrapPassword);
+ actual._wrapEndpointUri.should.equal(expectedWrapEndpointUri);
+ });
+
+ test('testCreateFromConnectionStringWithMissingServiceBusEndpointFail