CSharp/AzureTableCmdletBase.cs

namespace AzureStorageCmdlets
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Management.Automation;
    using System.Net;
    using System.IO;
    using System.Linq;
    using System.Xml.Linq;
    using System.Security;
    using System.Collections;
    using System.Collections.ObjectModel;
 
    public class AzureTableCmdletBase: AzureStorageCmdletBase
    {
        public override string Endpoint
        {
            get {
                return "http://" + StorageAccount + ".table.core.windows.net/";
            }
        }
 
        // Query entities. Returned entity list XML matching query filter.
        // Return true on success, false if not found, throw exception on error.
        protected string QueryEntities(string tableName, string partition, string row, string filter, string sort, string select, uint first)
        {
            return Retry<string>(delegate()
            {
                HttpWebRequest request;
                HttpWebResponse response;
 
                string entityXml = null;
 
                try
                {
                    string resource = tableName;
                    if (
                        !String.IsNullOrEmpty(row)
                        &&
                        !String.IsNullOrEmpty(partition)
                    )
                    {
                        resource += @"(PartitionKey=""" + partition + @""",RowKey=""" + row + @""")?";
                    }
                    else
                    {
                        resource += "()?";
                    }
                    if (!String.IsNullOrEmpty(sort))
                    {
                        resource += "$OrderBy=" + sort + "&";
                    }
                     
                    if (!String.IsNullOrEmpty(filter))
                    {
                        resource += "$filter=" + Uri.EscapeDataString(filter) + "&";
                    }
                    if (!String.IsNullOrEmpty(select))
                    {
                        resource += "$select=" + select + "&";
                    }
 
                    if (first > 0)
                    {
                        resource += "$top=" + first + "&";
                    }
                     
                    resource = resource.TrimEnd('&');
                    this.WriteVerbose("Creating Request for " + (Endpoint + resource));
                    SortedList<string, string> headers = new SortedList<string, string>();
                    if (! String.IsNullOrEmpty(nextTable)) {
                         
                        resource += ("&NextTableName=" + nextTable);
                        WriteVerbose("NextTable" + nextTable);
                    }
                    if (! String.IsNullOrEmpty(nextPart)) {
                         
                        resource += ("&NextPartitionKey=" + nextPart);
                        WriteVerbose("NextPart" + nextPart);
                    }
                    if (! String.IsNullOrEmpty(nextRow)) {
                         
                        resource += ("&NextRowKey=" + nextRow);
                        WriteVerbose("NextRow" + nextRow);
                    }
                    if (headers.Count == 0) {
                        headers = null;
                    }
                    request = CreateRESTRequest("GET", resource, null, headers, null, null);
                    nextRow = String.Empty;
                    nextPart = String.Empty;
                    request.Accept = "application/atom+xml,application/xml";
 
                    response = request.GetResponse() as HttpWebResponse;
 
                    if ((int)response.StatusCode == 200)
                    {
                        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                        {
                            string result = reader.ReadToEnd();
 
                            XNamespace ns = "http://www.w3.org/2005/Atom";
                            XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
 
                            XElement entry = XElement.Parse(result);
 
                            entityXml = entry.ToString();
                        }
                    }
 
                    response.Close();
                    string nextRowHeader = response.Headers["x-ms-continuation-NextRowKey"];
                    if (! (String.IsNullOrEmpty(nextRowHeader))) {
                        nextRow = nextRowHeader;
                    }
                    string nextPartHeader = response.Headers["x-ms-continuation-NextPartitionKey"];
                    if (! (String.IsNullOrEmpty(nextPartHeader))) {
                        nextPart = nextPartHeader;
                    }
                    string nextTableName = response.Headers["x-ms-continuation-NextTableName"];
                    if (! (String.IsNullOrEmpty(nextTableName))) {
                        nextTable = nextPartHeader;
                    }
                    return entityXml;
                }
                catch (WebException ex)
                {
                    WriteWebError(ex, "Table: " + tableName + " Filter: " + filter);
                    return string.Empty;
                }
            });
        }
 
        protected string nextRow;
        protected string nextPart;
        protected string nextTable;
 
        protected PSObject InsertEntity(string tableName, string partitionKey, string rowKey, PSObject obj, string author, string email, bool update, bool merge, bool excludeTableInfo)
        {
            return Retry<PSObject>(delegate()
            {
                HttpWebResponse response;
 
                try
                {
                    // Create properties list. Use reflection to retrieve properties from the object.
 
                    StringBuilder properties = new StringBuilder();
                    properties.Append(string.Format("<d:{0}>{1}</d:{0}>\n", "PartitionKey", partitionKey));
                    properties.Append(string.Format("<d:{0}>{1}</d:{0}>\n", "RowKey", rowKey));
 
                    string lastTypeName = obj.TypeNames.Last();
                    if (lastTypeName != "System.Object" && lastTypeName != "System.Management.Automation.PSObject")
                    {
                     
                        properties.Append(string.Format("<d:psTypeName>{0}</d:psTypeName>\n", SecurityElement.Escape(lastTypeName)));
                    }
                    foreach (PSPropertyInfo p in obj.Properties)
                    {
                        try
                        {
                             
                            string valueToInsert = (string)LanguagePrimitives.ConvertTo(p.Value, typeof(string));
                             
                            properties.Append(string.Format("<d:{0}>{1}</d:{0}>\n", p.Name, SecurityElement.Escape(valueToInsert)));
                        }
                        catch
                        {
 
                        }
                    }
 
                    string now = DateTime.UtcNow.ToString("o", System.Globalization.CultureInfo.InvariantCulture);
                    string id = String.Empty;
                    if (update || merge)
                    {
                        id = String.Format("http://{0}.table.core.windows.net/{1}(PartitionKey='{2}',RowKey='{3}')", StorageAccount, tableName, partitionKey, rowKey);
                    }
                    string requestBody = String.Format("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" +
                                          "<entry xmlns:d=\"http://schemas.microsoft.com/ado/2007/08/dataservices\"" +
                                          " xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\"" +
                                          " xmlns=\"http://www.w3.org/2005/Atom\"> " +
                                          " <title /> " +
                                          " <updated>{0}</updated> " +
                                          " <author>" +
                                          " <name/> " +
                                          " </author> " +
                                          " <id>{1}</id> " +
                                          " <content type=\"application/xml\">" +
                                          " <m:properties>" +
                                          "{2}" +
                                          " </m:properties>" +
                                          " </content> " +
                                          "</entry>",
                                          now,
                                          id,
                                          properties);
 
                    if (!String.IsNullOrEmpty(author))
                    {
                        if (!String.IsNullOrEmpty(email))
                        {
                            requestBody.Replace("<name/>", ("<name>" + SecurityElement.Escape(author) + "</name><email>" + SecurityElement.Escape(email) + "</email>"));
                        }
                        else
                        {
                            requestBody.Replace("<name/>", ("<name>" + SecurityElement.Escape(author) + "</name>"));
                        }
 
                    }
 
                    if (merge)
                    {
                        string resource = String.Format(tableName + "(PartitionKey='{0}',RowKey='{1}')", partitionKey, rowKey);
                        SortedList<string, string> headers = new SortedList<string, string>();
                        headers.Add("If-Match", "*");
                        response = CreateRESTRequest("MERGE", resource, requestBody, headers, String.Empty, String.Empty).GetResponse() as HttpWebResponse;
                    }
                    else if (update)
                    {
                        string resource = String.Format(tableName + "(PartitionKey='{0}',RowKey='{1}')", partitionKey, rowKey);
                        SortedList<string, string> headers = new SortedList<string, string>();
                        headers.Add("If-Match", "*");
 
                        response = CreateRESTRequest("PUT", resource, requestBody, headers, String.Empty, String.Empty).GetResponse() as HttpWebResponse;
                    }
                    else
                    {
                        response = CreateRESTRequest("POST", tableName, requestBody, null, String.Empty, String.Empty).GetResponse() as HttpWebResponse;
                    }
 
                    if (response.StatusCode == HttpStatusCode.Created)
                    {
                        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                        {
                            string result = reader.ReadToEnd();
 
                            XNamespace ns = "http://www.w3.org/2005/Atom";
                            XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
                            XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
                            return RecreateObject(result, ! excludeTableInfo, tableName).First();
                        }
                    }
                    response.Close();
 
                    return null;
                }
                catch (WebException ex)
                {
                    if (ex.Status == WebExceptionStatus.ProtocolError &&
                        ex.Response != null)
 
                        WriteError(
                            new ErrorRecord(
                                new InvalidOperationException(
                                    ((ex.Response as HttpWebResponse).StatusCode.ToString()) + "-- Table: " + tableName + "Partition: " + partitionKey + "Row: " + rowKey),
                                    "SetAzureTableCommand.WebError." + ((int)(ex.Response as HttpWebResponse).StatusCode).ToString(),
                                    ErrorCategory.InvalidOperation,
                                    this)
                                    );
                    return null;
                }
            });
        }
         
        protected IEnumerable ExpandObject(string atomXml, bool includeTableInfo, string fromTable)
        {
            if (String.IsNullOrEmpty(atomXml)) {
                yield break;
            }
            XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
            this.WriteDebug(atomXml);
             
            XElement entry = XElement.Parse(atomXml);
             
 
 
            foreach (XElement propertyGroup in entry.Descendants(m + "properties"))
            {
                PSObject returnObject = new PSObject();
                foreach (XElement element in propertyGroup.Descendants())
                {
                    if (element.Name.LocalName == "psTypeName")
                    {
                        returnObject.TypeNames.Clear();
                        foreach (string typename in element.Value.Split(',')) {
                            returnObject.TypeNames.Add(typename);
                        }
                         
                    } else if (element.Name.LocalName == "PartitionKey") {
                        if (!includeTableInfo) { continue;}
                        PSNoteProperty noteProperty = new PSNoteProperty("PartitionKey", element.Value);
                        try {
                            returnObject.Properties.Add(noteProperty);
                        } catch {
                            try {
                                // Remove the old and replace with the new
                                returnObject.Properties.Remove(noteProperty.Name);
                                returnObject.Properties.Add(noteProperty);
                            } catch {
                             
                            }
                        }
                    } else if (element.Name.LocalName == "RowKey") {
                       if (!includeTableInfo) { continue;}
                       PSNoteProperty noteProperty = new PSNoteProperty("RowKey", element.Value);
                       try {
                            returnObject.Properties.Add(noteProperty);
                       } catch {
                            try {
                                // Remove the old and replace with the new
                                returnObject.Properties.Remove(noteProperty.Name);
                                returnObject.Properties.Add(noteProperty);
                            } catch {
                             
                            }
                        }
                    } else if (element.Name.LocalName == "Timestamp") {
                        if (!includeTableInfo) { continue;}
                        try {
                            DateTime lastUpdated = DateTime.Parse(element.Value);
                            PSNoteProperty noteProperty = new PSNoteProperty("Timestamp", lastUpdated);
                            returnObject.Properties.Add(noteProperty);
                        } catch {
                            try {
                                // Remove the old and replace with the new
                                returnObject.Properties.Remove("Timestamp");
                                PSNoteProperty noteProperty = new PSNoteProperty("Timestamp", element.Value);
                                returnObject.Properties.Add(noteProperty);
                            } catch {
                             
                            }
                        }
                     
                    } else {
                        PSNoteProperty noteProperty = new PSNoteProperty(element.Name.LocalName, element.Value);
                        returnObject.Properties.Add(noteProperty);
                    }
                }
                if (includeTableInfo)
                {
                    try {
                        PSNoteProperty noteProperty = new PSNoteProperty("TableName", fromTable);
                        returnObject.Properties.Add(noteProperty);
                    } catch {
                     
                    }
                }
                yield return returnObject;
            }
                                                     
        }
 
        protected Collection<PSObject> RecreateObject(string atomXml, bool includeTableInfo, string fromTable)
        {
            if (String.IsNullOrEmpty(atomXml)) {
                return null;
            }
            Collection<PSObject> psObjects = new Collection<PSObject>();
            XNamespace ns = "http://www.w3.org/2005/Atom";
            XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices";
            XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
            this.WriteVerbose("Recreating Object From ATOM:" + Environment.NewLine + atomXml);
            XElement entry = XElement.Parse(atomXml);
             
             
 
            foreach (XElement propertyGroup in entry.Descendants(m + "properties"))
            {
                PSObject returnObject = new PSObject();
                foreach (XElement element in propertyGroup.Descendants())
                {
                    if (element.Name.LocalName == "psTypeName")
                    {
                        returnObject.TypeNames.Clear();
                        foreach (string typename in element.Value.Split(',')) {
                            returnObject.TypeNames.Add(typename);
                        }
                         
                    }
                    else if (element.Name.LocalName == "PartitionKey")
                    {
                        if (includeTableInfo)
                        {
                            returnObject.Properties.Remove("PartitionKey");
                            PSNoteProperty noteProperty = new PSNoteProperty("PartitionKey", element.Value);
                            returnObject.Properties.Add(noteProperty);
                        }
                    }
                    else if (element.Name.LocalName == "RowKey")
                    {
                        if (includeTableInfo)
                        {
                            returnObject.Properties.Remove("RowKey");
                            PSNoteProperty noteProperty = new PSNoteProperty("RowKey", element.Value);
                            returnObject.Properties.Add(noteProperty);
                        }
                    }
                    else if (element.Name.LocalName == "Timestamp")
                    {
                        if (includeTableInfo)
                        {
                            returnObject.Properties.Remove("Timestamp");
                            DateTime lastUpdated = (DateTime)LanguagePrimitives.ConvertTo(element.Value, typeof(DateTime));
                            PSNoteProperty noteProperty = new PSNoteProperty("Timestamp", element.Value);
                            returnObject.Properties.Add(noteProperty);
                        }
                    }
                    else
                    {
                        PSNoteProperty noteProperty = new PSNoteProperty(element.Name.LocalName, element.Value);
                        returnObject.Properties.Add(noteProperty);
                    }
                }
                if (includeTableInfo && !String.IsNullOrEmpty(fromTable))
                {
                    returnObject.Properties.Remove("TableName");
                    PSNoteProperty noteProperty = new PSNoteProperty("TableName", fromTable);
                    returnObject.Properties.Add(noteProperty);
                }
                psObjects.Add(returnObject);
            }
                                         
            return psObjects;
        }
 
        protected override bool IsTableStorage
        {
            get
            {
                return true;
            }
        }
    }
}