GitXplorerGitXplorer
p

RestCloudTable

public
1 stars
0 forks
0 issues

Commits

List of commits on branch master.
Unverified
bc0b414db1b29f05f2180451b320afc826acc28c

Completed server cert validation in all methods

ppeteraritchie committed 12 years ago
Unverified
14bd6a78996671eab4e45483a1b464bf34b10e45

Updated test comments

ppeteraritchie committed 12 years ago
Unverified
bf41c12c1ca4039ef4fb58de6ad17abbd885a5e3

renamed class

ppeteraritchie committed 12 years ago
Unverified
14508a61ea881419d01b6dbcac8eb71c6d05d68b

updated project for renamed file

ppeteraritchie committed 12 years ago
Unverified
487fc732369b267905d89cea3c36e26ea648ecc7

Updated readme

ppeteraritchie committed 12 years ago
Unverified
135f354e1fadb356dde75df506fc6bc4933c7bcb

Renamed ResetCloudTableTests, cleaned up some code

ppeteraritchie committed 12 years ago

README

The README file for this repository.

RestCloudTable

This sample project makes use of the Azure Table Storage REST API. It's intended to serve as an example of how you might access Azure Table storage with a specific server certificate validation policy.

Specifying server certificate validation policy

For the most part, operations occur with an instance of RestCloudTable. To override the server certificate validation, it's simply a matter of setting the ServerCertificateValidationCallback property with the delegate you want invoked.

For example, if you want to allow certs that have expired in the last two weeks, you could set the ServerCertificateValidationCallback to the following lambda statement:

	restTable.ServerCertificateValidationCallback =
		(chain, errors, sender, certificate, request, oldCallback) =>
		{
			if (errors == SslPolicyErrors.None) return true;
			if (errors == SslPolicyErrors.RemoteCertificateChainErrors)
			{
				var effectiveDate =
					DateTime.Parse(certificate.GetEffectiveDateString());
				var expirationDate =
					DateTime.Parse(certificate.GetExpirationDateString());
				if (effectiveDate < DateTime.Now || expirationDate.AddDays(14) > DateTime.Now)
				{
					return true;
				}
			}
			return false;
		};

With operations that occur through static methods on RestCloudTable, and optional delegate can be passed in. For example:

	var table = RestCloudTable
		.Create(accountName, accountKey, "myTable",
				(chain, errors, sender, certificate, request, oldCallback) =>
				{
				    if (errors == SslPolicyErrors.None) return true;
				    if (errors == SslPolicyErrors.RemoteCertificateChainErrors)
				    {
				        var effectiveDate =
				        	DateTime.Parse(certificate.GetEffectiveDateString());
				        var expirationDate =
				        	DateTime.Parse(certificate.GetExpirationDateString());
				        if (effectiveDate < DateTime.Now || expirationDate.AddDays(14) > DateTime.Now)
				        {
				        	return true;
				        }
				    }
				    return false;
				});

The sender argument is passed in by the networking subsystem, and may or may not be the HttpWebRequest associated with the request. Witin the RestCloudTable code, it passes in the actual HttpWebRequest that it used so that you can compare with the sender argument to see if this certificate validation applies to the current RestCloudTable request. Also, each time a reqest is made this callback is set in the network subsystem to reduce the risk (but not illiminate) race conditions of other threads replacing this "global" value. oldCallback is the value of the callback that was in place when the request was started. If you decide it's useful to chain through to any existing callbacks, you can simply invoke this delegate.

The API

The API is similar, but different, to the .NET API. There's a single class RestCloudTable that contains all the table operations that can be performed.

Those operations are: Create table Delete table Insert entity Delete entity Update (Replace) entity Merge entity Query entities Retrieve entity

In general, this API re-uses some of the .NET API. Some things that don't access Azure directly are re-used; like TableEntity, TableQuery, and UriQueryBuilder.

For the most part, working with this API is the same as working with the .NET API, everything works with TableEntitys. You create entities classes that you want to store in Azure Table storage and derive them from TableEntity and things like PartitionKey and RowKey come for free.

Create table

var table = RestCloudTable.Create(accountName, accountKey, "myTableName");

If the table already exists a TableExistsException will be thrown.

Delete table

RestCloudTable.Delete(accountName, accountKey, "myTableName");

If the table already exists a TableExistsException will be thrown.

Insert entity

	restTable = new RestCloudTable(accountName,
			                        accountKey,
			                        TableName);
	restTable.InsertEntity(new ContactEntity("firstName", "lastName")
			                {
			                Email="email",
			                PhoneNumber = "phoneNumber"
			                });

If the entity already exists, EntityAlreadyExistsException will be thrown.

Delete entity

	restTable = new RestCloudTable(accountName,
			                        accountKey,
			                        TableName);
	var entity = restTable.RetrieveEntity<ContactEntity>("Ritchie", "Peter");
	entity.ETag = entity.ETag ?? "*";

	restTable.DeleteEntity("Ritchie", "Peter", entity.ETag);

In the same way as the .NET API requires an ETag, so does the REST API. Use "*" as an ETag to avoid concurrency conflicts. See the Azure Table Storage documentation for more information on ETag

Update (Replace) entity

	restTable = new RestCloudTable(accountName,
			                        accountKey,
			                        TableName);

	var entity = restTable.RetrieveEntity<ContactEntity>("Ritchie", "Peter");
	entity.ETag = entity.ETag ?? "*";
	entity.PhoneNumber = "613-231-1165";

	restTable.UpdateEntity(entity);

If the entity didn't exist the method simply returns with no exception.

Merge entity

	restTable = new RestCloudTable(accountName,
			                        accountKey,
			                        TableName);
	var entity = restTable.RetrieveEntity<ContactEntity>("Ritchie", "Peter");
	entity.ETag = entity.ETag ?? "*";
	entity.PhoneNumber = "613-231-1165";

	restTable.MergeEntity(entity);

If the entity didn't exist the method simply returns with no exception.

Query entities

	restTable = new RestCloudTable(accountName,
			                        accountKey,
			                        TableName);
	TableQuery<ContactEntity> tableQuery =
		new TableQuery<ContactEntity>().Where(
			TableQuery.CombineFilters(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "Ritchie"),
					                    TableOperators.And,
					                    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, "Peter")));

	IEnumerable<ContactEntity> entities = restTable.QueryEntities(tableQuery);

Retrieve entity

	restTable = new RestCloudTable(accountName,
			                        accountKey,
			                        TableName);
	var entity = restTable.RetrieveEntity<ContactEntity>("Ritchie", "Peter");

If the entity does not exist, null is returned.

For more detail on API usage, see the tests.

Running the tests

As you might imagine, I did not include my account name and account key in the project commited in GitHub. In order to run the tests you need to create two files privateAppSettings.config and privateConnectionStrings.config. These two files contain the private settings used by the tests to connect to your Azure Table storage instance.

Example privateAppSettings.config

<?xml version="1.0" encoding="utf-8" ?>
<appSettings>
	<add key="tableName" value="testTableName" />
	<add key="accountName" value="myAccountName" />
</appSettings>

Eample privateConnectionStrings.config

<connectionStrings>
	<add name="Azure"
	 connectionString="DefaultEndpointsProtocol=https;AccountName=myAccountName;AccountKey=myAccountKey" />
</connectionStrings>

Until those config files are created on your local computer the tests will not run.

The tests assume you're hitting a production Azure server, it doesn't use a local emulated storage service.