using System;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Net;
using System.IO;
using System.Xml;
using System.Collections;
namespace Finsel.AzureCommands
{
///
/// Used for interfacing with the Microsoft's Azure Table Storage
///
public class AzureTableStorage
{
azureCommon ac = new azureCommon();
azureDirect ad = new azureDirect();
private Authentication pAuth = new Authentication();
///
/// Object that stores information needed to access an instance of
/// Azure Table Storage
///
public Authentication auth { get { return pAuth; } set { pAuth = value; pAuth.EndPoint = string.Format(EndpointFormat, pAuth.Account); ad.auth = pAuth; } }
///
/// Create a new AzureCommands object
/// This requires filling in the .auth object
///
public AzureTableStorage()
{
}
///
/// Create a new AzureTableStorage object and populate the Authentication information
///
/// The account is generally the first part of the Endpoint.
/// The Endpoint for Azure Table Storage as defined for your account
/// The SharedKey for access
/// Shared
public AzureTableStorage(string account, string endPoint, string sharedKey, string keyType)
{
pAuth = new Authentication(account, string.Format(EndpointFormat, account), sharedKey);
ad.auth = pAuth;
}
///
/// Create a new AzureTableStorage object and populate the Authentication information
///
/// takes an Azure Authentication object
public AzureTableStorage(Authentication pAuth)
{
auth = pAuth;
}
// In the current release, this is the same for all clients, differing only in the
// opening subdomain
private string EndpointFormat = "http://{0}.table.core.windows.net/";
///
/// Results of request
///
public string ETag = string.Empty;
private string contentType = "application/atom+xml";
private DateTime requestDate = DateTime.UtcNow;
private string contentMD5 = string.Empty;
private string authHeader = string.Empty;
private string method = string.Empty;
///
/// Get a list of tables defined for an ATS instance
///
/// An azureResults showing the results of the request.
public azureResults GetTableList()
{
return Tables(cmdType.get, "");
}
///
/// Tables handles table level operations against Azure Table Storage
///
/// The type of operation you want to commit: Delete, Post, Put, Get
/// Name of the table to perform the command agains
/// An azureResults showing the results of the request.
public azureResults Tables(cmdType cmd, string tableName)
{
return Tables(cmd, tableName, "");
}
///
/// Tables handles table level operations against Azure Table Storage
///
/// The type of operation you want to commit: Delete, Post, Put, Get
/// Name of the table to perform the command agains
/// Uri Parameters to include
/// An azureResults showing the results of the request.
public azureResults Tables(cmdType cmd, string tableName, string parameters)
{
azureResults retVal = new azureResults();
HttpStatusCode success = HttpStatusCode.NotImplemented;
Hashtable headers = new Hashtable();
try
{
StringBuilder sb = new StringBuilder();
string sendBody = string.Empty;
string rtnBody = string.Empty;
string requestUrl = string.Format(CultureInfo.CurrentCulture, "{0}{1}", auth.EndPoint, "Tables");
requestDate = DateTime.UtcNow;
switch (cmd)
{
case cmdType.get:
method = "GET";
string allData = tableName;
if (tableName != string.Empty)
{
if (parameters == string.Empty)
{
requestUrl = requestUrl.Replace("Tables", string.Format(CultureInfo.CurrentCulture, "{0}()", allData));
}
else
{
requestUrl += string.Format(CultureInfo.CurrentCulture, "('{0}')", allData);
if (!parameters.StartsWith("?"))
parameters = "?" + parameters;
requestUrl += string.Format(CultureInfo.CurrentCulture, "{0}", parameters);
}
}
success = HttpStatusCode.OK;
break;
case cmdType.post:
method = "POST";
// build valid Atom document
sendBody = string.Format(createTableXml, requestDate, tableName);
success= HttpStatusCode.Created;
break;
case cmdType.delete:
method = "DELETE";
requestUrl += string.Format(CultureInfo.CurrentCulture, "('{0}')", tableName);
success = HttpStatusCode.NoContent;
break;
default:
break;
}
headers.Add("Content-Type", contentType);
retVal = ad.ProcessRequest(cmd, requestUrl, sendBody, headers);
if (success == HttpStatusCode.NotImplemented)
{
retVal.Succeeded = false;
retVal.StatusCode = success;
}
else
retVal.Succeeded = (retVal.StatusCode == success);
}
catch (HttpException hex)
{
retVal.StatusCode = (HttpStatusCode)hex.GetHttpCode();
retVal.Succeeded = false;
retVal.Body = hex.GetHtmlErrorMessage();
}
catch (Exception ex)
{
retVal.StatusCode = HttpStatusCode.SeeOther;
retVal.Body = ex.ToString();
retVal.Succeeded = false;
}
return retVal;
}
///
/// Entities returns information about entities. Uses If-Match of * and forces overwrites/deletes of data.
///
/// The type of command you want to execute
/// Table name
/// Partition Key
/// Row Key
/// Additional Data for Updates, Adds, Merges
/// Any Uri parameters to be passed in. comp=list can be passed in to get a list.
///
public azureResults Entities(cmdType cmd, string tableName, string PartitionKey, string rowKey, string docData, string parameters)
{
return Entities(cmd, tableName, PartitionKey, rowKey, docData, parameters, "*");
}
///
/// Entities returns information about entities
///
/// The type of command you want to execute
/// Table name
/// Partition Key
/// Row Key
/// Additional Data for Updates, Adds, Merges
/// The entity tag or * to execute an update/merge/delete regardless of the entity tag
/// Any Uri parameters to be passed in. comp=list can be passed in to get a list.
///
public azureResults Entities(cmdType cmd, string tableName, string PartitionKey, string rowKey, string docData, string parameters, string IfMatch)
{
Hashtable headers = new Hashtable();
azureResults retVal = new azureResults();
HttpStatusCode success = HttpStatusCode.NotImplemented;
try
{
string sendBody = string.Empty;
string readBody = string.Empty;
string requestUrl = string.Format(CultureInfo.CurrentCulture, "{0}{1}", auth.EndPoint, tableName);
requestDate = DateTime.UtcNow;
switch (cmd)
{
case cmdType.get:
method = "GET";
IfMatch = string.Empty;
sendBody = string.Empty;
if (PartitionKey != string.Empty && rowKey != string.Empty)
{
requestUrl += string.Format(CultureInfo.CurrentCulture, "(PartitionKey='{0}',RowKey='{1}')", PartitionKey, rowKey);
}
else if (PartitionKey != string.Empty)
{
requestUrl += string.Format(CultureInfo.CurrentCulture, "(PartitionKey='{0}')", PartitionKey);
}
else
{
requestUrl += string.Format(CultureInfo.CurrentCulture, "()");
}
if (parameters != string.Empty)
{
if (!parameters.StartsWith("?"))
parameters = "?" + parameters;
requestUrl += string.Format(CultureInfo.CurrentCulture, parameters);
}
// do GET
success = HttpStatusCode.OK;
break;
case cmdType.post:
method = "POST";
// accept input doc and parse into valid Atom for Azure Tables
readBody = docData;
IfMatch = "";
sendBody = string.Format(createEntityXml, requestDate, readBody);
sendBody = string.Format(sendBody, PartitionKey, rowKey);
success = HttpStatusCode.Created;
break;
case cmdType.put:
method = "PUT";
requestUrl += string.Format(CultureInfo.CurrentCulture, "(PartitionKey='{0}',RowKey='{1}')", PartitionKey, rowKey);
// accept input doc and parse into valid Atom for Azure Tables
sendBody = docData;
sendBody = string.Format(updateEntityXml, tableName, PartitionKey, rowKey, requestDate, sendBody, this.ETag.Replace(@"""", """), auth.Account);
success = HttpStatusCode.NoContent;
break;
case cmdType.merge:
method = "MERGE";
requestUrl += string.Format(CultureInfo.CurrentCulture, "(PartitionKey='{0}',RowKey='{1}')", PartitionKey, rowKey);
// accept input doc and parse into valid Atom for Azure Tables
sendBody = docData;
sendBody = string.Format(updateEntityXml, tableName, PartitionKey, rowKey, requestDate, sendBody, this.ETag.Replace(@"""", """), auth.Account);
success = HttpStatusCode.NoContent;
break;
case cmdType.delete:
method = "DELETE";
sendBody = string.Empty;
requestUrl += string.Format(CultureInfo.CurrentCulture, "(PartitionKey='{0}',RowKey='{1}')", PartitionKey, rowKey);
success = HttpStatusCode.NoContent;
break;
default:
retVal.Succeeded = false;
retVal.StatusCode = HttpStatusCode.NotImplemented;
break;
}
headers.Add("Content-Type", contentType);
if (IfMatch != string.Empty)
headers.Add("If-Match", IfMatch);
retVal = ad.ProcessRequest(cmd, requestUrl, sendBody, headers);
if (success == HttpStatusCode.NotImplemented)
{
retVal.Succeeded = false;
retVal.StatusCode = success;
}
else
retVal.Succeeded = (retVal.StatusCode == success);
}
catch (HttpException hex)
{
retVal.Succeeded = false;
retVal.StatusCode = (HttpStatusCode)hex.GetHttpCode();
retVal.Body = hex.GetHtmlErrorMessage();
}
catch (Exception ex)
{
retVal.StatusCode = HttpStatusCode.SeeOther;
retVal.Body = ex.ToString();
retVal.Succeeded = false;
}
// Note: Body may contain data elements with XML... will need to use
// Uri.UnescapeDataString() to format correctly
return retVal;
}
// stub create table body
string createTableXml = @"
{0:yyyy-MM-ddTHH:mm:ss.fffffffZ}{1}";
// stub create entity body
string createEntityXml = @"
{0:yyyy-MM-ddTHH:mm:ss.fffffffZ}
{1}
";
/* sample body to pass in via string or file ref
{0}{1}Mountain View23200.23c9da6455-213d-42c9-9a79-3e9149a578332008-07-10T00:00:00true2550001-01-01T00:00:00
*/
// stub update entity body
string updateEntityXml = @"
http://{6}.table.core.windows.net/{0}(PartitionKey='{1}',RowKey='{2}'){3:yyyy-MM-ddTHH:mm:ss.fffffffZ}
{4}
";
}
}