Adding StorageFolder poco object, and support for folders at the container level.

Implements: blueprint storage-folder-support
Change-Id: I8cf14854c3eabf83c385b6fb780853defd6d6842
This commit is contained in:
Wayne Foley
2014-03-27 16:29:09 -07:00
parent d6365c3fad
commit a469ad406c
11 changed files with 719 additions and 2 deletions

View File

@@ -92,6 +92,8 @@
<Compile Include="OpenstackClientManagerTests.cs" /> <Compile Include="OpenstackClientManagerTests.cs" />
<Compile Include="OpenstackServiceClientManagerTests.cs" /> <Compile Include="OpenstackServiceClientManagerTests.cs" />
<Compile Include="Storage\StorageServiceClientDefinitionTests.cs" /> <Compile Include="Storage\StorageServiceClientDefinitionTests.cs" />
<Compile Include="Storage\StorageFolderTests.cs" />
<Compile Include="Storage\StorageFolderPayloadConverterTests.cs" />
<Compile Include="TestOpenstackRegionResolver.cs" /> <Compile Include="TestOpenstackRegionResolver.cs" />
<Compile Include="TestOpenstackServiceEndpointResolver.cs" /> <Compile Include="TestOpenstackServiceEndpointResolver.cs" />
<Compile Include="ObjectExtentionsTests.cs" /> <Compile Include="ObjectExtentionsTests.cs" />

View File

@@ -253,6 +253,111 @@ namespace Openstack.Test.Storage
Assert.AreEqual(1, container.Objects.ToList().Count()); Assert.AreEqual(1, container.Objects.ToList().Count());
} }
[TestMethod]
public void CanParseContainerWithValidJsonPayloadWithNestedFoldersAndObjects()
{
var containerName = "TestContainer";
var validObjectJson = @"[
{
""hash"": ""d41d8cd98f00b204e9800998ecf8427e"",
""last_modified"": ""2014-03-27T20:57:11.150910"",
""bytes"": 0,
""name"": ""a/"",
""content_type"": ""application/octet-stream""
},
{
""hash"": ""d41d8cd98f00b204e9800998ecf8427e"",
""last_modified"": ""2014-03-27T20:57:36.676350"",
""bytes"": 0,
""name"": ""a/b/"",
""content_type"": ""application/octet-stream""
},
{
""hash"": ""437b930db84b8079c2dd804a71936b5f"",
""last_modified"": ""2014-03-27T20:58:36.676620"",
""bytes"": 9,
""name"": ""a/b/b"",
""content_type"": ""text/plain;charset=UTF-8""
},
{
""hash"": ""437b930db84b8079c2dd804a71936b5f"",
""last_modified"": ""2014-03-27T20:58:43.935540"",
""bytes"": 9,
""name"": ""a/b/c"",
""content_type"": ""text/plain;charset=UTF-8""
},
{
""hash"": ""437b930db84b8079c2dd804a71936b5f"",
""last_modified"": ""2014-03-27T20:58:54.142580"",
""bytes"": 9,
""name"": ""a/b/c/object3"",
""content_type"": ""text/plain;charset=UTF-8""
},
{
""hash"": ""437b930db84b8079c2dd804a71936b5f"",
""last_modified"": ""2014-03-27T20:58:25.771530"",
""bytes"": 9,
""name"": ""a/object2"",
""content_type"": ""text/plain;charset=UTF-8""
},
{
""hash"": ""d41d8cd98f00b204e9800998ecf8427e"",
""last_modified"": ""2014-03-27T20:57:47.122360"",
""bytes"": 0,
""name"": ""a/x/"",
""content_type"": ""application/octet-stream""
},
{
""hash"": ""437b930db84b8079c2dd804a71936b5f"",
""last_modified"": ""2014-03-27T20:58:15.696360"",
""bytes"": 9,
""name"": ""object1"",
""content_type"": ""text/plain;charset=UTF-8""
}
]";
var converter = new StorageContainerPayloadConverter();
var headers = new HttpHeadersAbstraction
{
{"X-Container-Bytes-Used", "45"},
{"X-Container-Object-Count", "8"}
};
var container = converter.Convert(containerName, headers, validObjectJson);
Assert.IsNotNull(container);
Assert.AreEqual(containerName, container.Name);
Assert.AreEqual(45, container.TotalBytesUsed);
Assert.AreEqual(8, container.TotalObjectCount);
Assert.AreEqual(8, container.Objects.ToList().Count());
Assert.IsTrue(container.Objects.ToList().Any(o => o.Name == "object1"));
var folders = container.Folders.ToList();
Assert.AreEqual(1, folders.Count());
var aNode = folders.First();
Assert.AreEqual("a", aNode.Name);
Assert.AreEqual(2, aNode.Folders.Count);
Assert.AreEqual(1, aNode.Objects.Count);
Assert.IsTrue(aNode.Objects.Any(f => f.Name == "a/object2"));
var xNode = aNode.Folders.First(f => f.Name == "x");
Assert.AreEqual(0, xNode.Folders.Count);
Assert.AreEqual(0, xNode.Objects.Count);
var bNode = aNode.Folders.First(f => f.Name == "b");
Assert.AreEqual(1, bNode.Folders.Count);
Assert.AreEqual(2, bNode.Objects.Count);
Assert.IsTrue(bNode.Folders.Any(f => f.Name == "c"));
Assert.IsTrue(bNode.Objects.Any(f => f.Name == "a/b/c"));
Assert.IsTrue(bNode.Objects.Any(f => f.Name == "a/b/b"));
var cNode = bNode.Folders.First(f => f.Name == "c");
Assert.AreEqual(0, cNode.Folders.Count);
Assert.AreEqual(1, cNode.Objects.Count);
Assert.IsTrue(cNode.Objects.Any(f => f.Name == "a/b/c/object3"));
}
[TestMethod] [TestMethod]
[ExpectedException(typeof(HttpParseException))] [ExpectedException(typeof(HttpParseException))]
public void CannotParseContainerWithMissingBytesUsedHeader() public void CannotParseContainerWithMissingBytesUsedHeader()

View File

@@ -0,0 +1,265 @@
// /* ============================================================================
// Copyright 2014 Hewlett Packard
//
// 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.
// ============================================================================ */
namespace Openstack.Test.Storage
{
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Openstack.Storage;
[TestClass]
public class StorageFolderPayloadConverterTests
{
[TestMethod]
public void CanAddFolderWithNestedFolders()
{
var objects = new List<StorageObject>() { new StorageObject("a/b/c/d/","a") };
var converter = new StorageFolderPayloadConverter();
var folders = converter.Convert(objects).ToList();
Assert.AreEqual(1,folders.Count);
Assert.AreEqual("a",folders[0].Name);
Assert.AreEqual(1, folders[0].Folders.Count);
Assert.AreEqual("b", folders[0].Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.Count);
Assert.AreEqual("c", folders[0].Folders.First().Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.First().Folders.Count);
Assert.AreEqual("d", folders[0].Folders.First().Folders.First().Folders.First().Name);
}
[TestMethod]
public void CanAddFolderWithNestedFoldersAndDuplicateNames()
{
var objects = new List<StorageObject>() { new StorageObject("a/c/c/c/", "a") };
var converter = new StorageFolderPayloadConverter();
var folders = converter.Convert(objects).ToList();
Assert.AreEqual(1, folders.Count);
Assert.AreEqual("a", folders[0].Name);
Assert.AreEqual(1, folders[0].Folders.Count);
Assert.AreEqual("c", folders[0].Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.Count);
Assert.AreEqual("c", folders[0].Folders.First().Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.First().Folders.Count);
Assert.AreEqual("c", folders[0].Folders.First().Folders.First().Folders.First().Name);
}
[TestMethod]
public void CanAddSingleFolder()
{
var objects = new List<StorageObject>() { new StorageObject("a/", "a") };
var converter = new StorageFolderPayloadConverter();
var folders = converter.Convert(objects).ToList();
Assert.AreEqual(1, folders.Count);
Assert.AreEqual("a", folders[0].Name);
Assert.AreEqual(0, folders[0].Folders.Count);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void CanAddFolderWithNullObjectList()
{
var converter = new StorageFolderPayloadConverter();
converter.Convert(null);
}
[TestMethod]
public void CanAddNestedFoldersWhenRootExists()
{
var objects = new List<StorageObject>() { new StorageObject("a/", "a"), new StorageObject("a/b/c/d/", "a") };
var converter = new StorageFolderPayloadConverter();
var folders = converter.Convert(objects).ToList();
Assert.AreEqual(1, folders.Count);
Assert.AreEqual("a", folders[0].Name);
Assert.AreEqual(1, folders[0].Folders.Count);
Assert.AreEqual("b", folders[0].Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.Count);
Assert.AreEqual("c", folders[0].Folders.First().Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.First().Folders.Count);
Assert.AreEqual("d", folders[0].Folders.First().Folders.First().Folders.First().Name);
}
[TestMethod]
public void CanAddNestedFoldersWhenRootExistsWithObjectsAtLeaf()
{
var objects = new List<StorageObject>() { new StorageObject("a/b/c/d/foo", "a"), new StorageObject("a/b/c/d/bar", "a") };
var converter = new StorageFolderPayloadConverter();
var folders = converter.Convert(objects).ToList();
Assert.AreEqual(1, folders.Count);
Assert.AreEqual("a", folders[0].Name);
Assert.AreEqual(1, folders[0].Folders.Count);
Assert.AreEqual("b", folders[0].Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.Count);
Assert.AreEqual("c", folders[0].Folders.First().Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.First().Folders.Count);
var leaf = folders[0].Folders.First().Folders.First().Folders.First();
Assert.AreEqual("d", leaf.Name);
Assert.AreEqual(2,leaf.Objects.Count);
Assert.IsTrue(leaf.Objects.Any(o => o.Name == "a/b/c/d/foo"));
Assert.IsTrue(leaf.Objects.Any(o => o.Name == "a/b/c/d/bar"));
}
[TestMethod]
public void CanAddNestedFoldersWhenRootExistsWithObjectsAtLeafAndRoot()
{
var objects = new List<StorageObject>() { new StorageObject("a/b/c/d/foo", "a"), new StorageObject("a/b/c/d/bar", "a"), new StorageObject("xyz", "a") };
var converter = new StorageFolderPayloadConverter();
var folders = converter.Convert(objects).ToList();
Assert.AreEqual(1, folders.Count);
Assert.AreEqual("a", folders[0].Name);
Assert.AreEqual(1, folders[0].Folders.Count);
Assert.AreEqual("b", folders[0].Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.Count);
Assert.AreEqual("c", folders[0].Folders.First().Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.First().Folders.Count);
var leaf = folders[0].Folders.First().Folders.First().Folders.First();
Assert.AreEqual("d", leaf.Name);
Assert.AreEqual(2, leaf.Objects.Count);
Assert.IsTrue(leaf.Objects.Any(o => o.Name == "a/b/c/d/foo"));
Assert.IsTrue(leaf.Objects.Any(o => o.Name == "a/b/c/d/bar"));
}
[TestMethod]
public void CanAddNestedFoldersWhenRootExistsWithObjectsAtLeafAndLongNameAtRoot()
{
var objects = new List<StorageObject>() { new StorageObject("a/b/c/d/foo", "a"), new StorageObject("a/b/c/d/bar", "a"), new StorageObject("thiswillsorttothetopofthelist", "a") };
var converter = new StorageFolderPayloadConverter();
var folders = converter.Convert(objects).ToList();
Assert.AreEqual(1, folders.Count);
Assert.AreEqual("a", folders[0].Name);
Assert.AreEqual(1, folders[0].Folders.Count);
Assert.AreEqual("b", folders[0].Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.Count);
Assert.AreEqual("c", folders[0].Folders.First().Folders.First().Name);
Assert.AreEqual(1, folders[0].Folders.First().Folders.First().Folders.Count);
var leaf = folders[0].Folders.First().Folders.First().Folders.First();
Assert.AreEqual("d", leaf.Name);
Assert.AreEqual(2, leaf.Objects.Count);
Assert.IsTrue(leaf.Objects.Any(o => o.Name == "a/b/c/d/foo"));
Assert.IsTrue(leaf.Objects.Any(o => o.Name == "a/b/c/d/bar"));
}
[TestMethod]
public void CanAddNestedFoldersWhenRootExistsWithObjectsAtManyLevels()
{
var objects = new List<StorageObject>() { new StorageObject("a/b/c/d/foo", "a"),
new StorageObject("a/b/bar", "a"),
new StorageObject("a/b/c/beans", "a"),
new StorageObject("a/string", "a"),
new StorageObject("a/b/c/d/", "a") ,
new StorageObject("a/b/c/", "a") ,
new StorageObject("a/", "a")
};
var converter = new StorageFolderPayloadConverter();
var folders = converter.Convert(objects).ToList();
var aNode = folders[0];
var bNode = folders[0].Folders.First();
var cNode = folders[0].Folders.First().Folders.First();
var dNode = folders[0].Folders.First().Folders.First().Folders.First();
Assert.AreEqual(1, folders.Count);
Assert.AreEqual("a", aNode.Name);
Assert.AreEqual(1, aNode.Folders.Count);
Assert.AreEqual(1, aNode.Objects.Count);
Assert.IsTrue(aNode.Objects.Any(o => o.Name == "a/string"));
Assert.AreEqual("b", bNode.Name);
Assert.AreEqual("a/b", bNode.FullName);
Assert.AreEqual(1, bNode.Folders.Count);
Assert.AreEqual(1, bNode.Objects.Count);
Assert.IsTrue(bNode.Objects.Any(o => o.Name == "a/b/bar"));
Assert.AreEqual("c", cNode.Name);
Assert.AreEqual("a/b/c", cNode.FullName);
Assert.AreEqual(1, cNode.Folders.Count);
Assert.AreEqual(1, cNode.Objects.Count);
Assert.IsTrue(cNode.Objects.Any(o => o.Name == "a/b/c/beans"));
Assert.AreEqual("d", dNode.Name);
Assert.AreEqual("a/b/c/d", dNode.FullName);
Assert.AreEqual(1, dNode.Objects.Count);
Assert.IsTrue(dNode.Objects.Any(o => o.Name == "a/b/c/d/foo"));
}
[TestMethod]
public void CanConvertFolders()
{
var objects = new List<StorageObject>()
{
new StorageObject("a", "a"),
new StorageObject("a/", "a"),
new StorageObject("b//", "a"),
new StorageObject("//a/", "a"),
new StorageObject("a/b/", "a", DateTime.Now, "12345", 100, "application/directory")
};
var converter = new StorageFolderPayloadConverter();
var resp = converter.Convert(objects).ToList();
Assert.AreEqual(1, resp.Count);
var aNode = resp.First();
Assert.AreEqual("a", aNode.Name);
Assert.AreEqual(1, aNode.Folders.Count);
Assert.AreEqual(0, aNode.Objects.Count);
var bNode = aNode.Folders.First();
Assert.AreEqual("b", bNode.Name);
Assert.AreEqual("a/b", bNode.FullName);
Assert.AreEqual(0, bNode.Folders.Count);
Assert.AreEqual(0, bNode.Objects.Count);
Assert.AreEqual(5, objects.Count);
}
[TestMethod]
public void CanConvertFoldersWithNoInputObjects()
{
var objects = new List<StorageObject>();
var converter = new StorageFolderPayloadConverter();
var resp = converter.Convert(objects).ToList();
Assert.AreEqual(0, resp.Count);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void CannotConvertFoldersWithNullObjectList()
{
var converter = new StorageFolderPayloadConverter();
var resp = converter.Convert(null);
}
}
}

View File

@@ -0,0 +1,148 @@
// /* ============================================================================
// Copyright 2014 Hewlett Packard
//
// 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.
// ============================================================================ */
namespace Openstack.Test.Storage
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Openstack.Storage;
[TestClass]
public class StorageFolderTests
{
[TestMethod]
public void CanCreateFolder()
{
var folder = new StorageFolder("MyFolder", new List<StorageFolder>());
Assert.AreEqual("MyFolder", folder.FullName);
Assert.AreEqual("MyFolder", folder.Name);
Assert.AreEqual(0,folder.Folders.Count());
}
[TestMethod]
public void CanCreateFolderWithLeadingSlashes()
{
var expectedFolderName = "MyFolder";
var folder = new StorageFolder("//MyFolder", new List<StorageFolder>());
Assert.AreEqual(expectedFolderName, folder.FullName);
Assert.AreEqual(expectedFolderName, folder.Name);
Assert.AreEqual(0, folder.Folders.Count());
}
[TestMethod]
public void CanCreateFolderWithTrailingSlashes()
{
var expectedFolderName = "MyFolder";
var folder = new StorageFolder("MyFolder//", new List<StorageFolder>());
Assert.AreEqual(expectedFolderName, folder.FullName);
Assert.AreEqual(expectedFolderName, folder.Name);
Assert.AreEqual(0, folder.Folders.Count());
}
[TestMethod]
public void CanCreateFolderWithMultipleFoldersInName()
{
var expectedFolderName = "MyFolder";
var folder = new StorageFolder("//Some/Folder/MyFolder", new List<StorageFolder>());
Assert.AreEqual("Some/Folder/MyFolder", folder.FullName);
Assert.AreEqual(expectedFolderName, folder.Name);
Assert.AreEqual(0, folder.Folders.Count());
}
[TestMethod]
public void CanExtractFolderNameWithNoSlashes()
{
var expectedFolderName = "myFolder";
var folderName = StorageFolder.ExtractFolderName("myFolder");
Assert.AreEqual(expectedFolderName, folderName);
}
[TestMethod]
public void CanExtractFolderNameWithLeadingSlash()
{
var expectedFolderName = "myFolder";
var folderName = StorageFolder.ExtractFolderName("/myFolder");
Assert.AreEqual(expectedFolderName, folderName);
}
[TestMethod]
public void CanExtractFolderNameWithTrailingSlash()
{
var expectedFolderName = "myFolder";
var folderName = StorageFolder.ExtractFolderName("myFolder/");
Assert.AreEqual(expectedFolderName, folderName);
}
[TestMethod]
public void CanExtractFolderNameWithTrailingAndLeadingSlash()
{
var expectedFolderName = "myFolder";
var folderName = StorageFolder.ExtractFolderName("/myFolder/");
Assert.AreEqual(expectedFolderName, folderName);
}
[TestMethod]
public void CanExtractFolderNameWithJustSlash()
{
var expectedFolderName = string.Empty;
var folderName = StorageFolder.ExtractFolderName("/");
Assert.AreEqual(expectedFolderName, folderName);
}
[TestMethod]
public void CanExtractFolderNameWithDoubleLeadingSlash()
{
var expectedFolderName = "MyFolder";
var folderName = StorageFolder.ExtractFolderName("//MyFolder");
Assert.AreEqual(expectedFolderName, folderName);
}
[TestMethod]
public void CanExtractFolderNameWithDoubleTrailingSlash()
{
var expectedFolderName = "MyFolder";
var folderName = StorageFolder.ExtractFolderName("MyFolder//");
Assert.AreEqual(expectedFolderName, folderName);
}
[TestMethod]
public void CanExtractFolderNameWithDoubleTrailingAndLeadingSlashes()
{
var expectedFolderName = "MyFolder";
var folderName = StorageFolder.ExtractFolderName("//MyFolder//");
Assert.AreEqual(expectedFolderName, folderName);
}
[TestMethod]
public void CanExtractFolderNameWithMultipleFolders()
{
var expectedFolderName = "MyFolder";
var folderName = StorageFolder.ExtractFolderName("Folder1/Folder2/Folder3/MyFolder");
Assert.AreEqual(expectedFolderName, folderName);
}
[TestMethod]
public void CanExtractFolderNameWithMultipleFoldersAndSlashes()
{
var expectedFolderName = "MyFolder";
var folderName = StorageFolder.ExtractFolderName("Folder1//Folder2/Folder3//MyFolder/");
Assert.AreEqual(expectedFolderName, folderName);
}
}
}

View File

@@ -90,6 +90,7 @@
<Compile Include="ServiceRegistrar.cs" /> <Compile Include="ServiceRegistrar.cs" />
<Compile Include="Storage\ContainerNameValidator.cs" /> <Compile Include="Storage\ContainerNameValidator.cs" />
<Compile Include="Storage\IStorageAccountPayloadConverter.cs" /> <Compile Include="Storage\IStorageAccountPayloadConverter.cs" />
<Compile Include="Storage\IStorageFolderPayloadConverter.cs" />
<Compile Include="Storage\IStorageServiceClient.cs" /> <Compile Include="Storage\IStorageServiceClient.cs" />
<Compile Include="Storage\IStorageContainerPayloadConverter.cs" /> <Compile Include="Storage\IStorageContainerPayloadConverter.cs" />
<Compile Include="Storage\IStorageObjectPayloadConverter.cs" /> <Compile Include="Storage\IStorageObjectPayloadConverter.cs" />
@@ -97,6 +98,8 @@
<Compile Include="Storage\IStorageServicePocoClientFactory.cs" /> <Compile Include="Storage\IStorageServicePocoClientFactory.cs" />
<Compile Include="Storage\IStorageServiceRestClient.cs" /> <Compile Include="Storage\IStorageServiceRestClient.cs" />
<Compile Include="Storage\IStorageServiceRestClientFactory.cs" /> <Compile Include="Storage\IStorageServiceRestClientFactory.cs" />
<Compile Include="Storage\StorageFolder.cs" />
<Compile Include="Storage\StorageFolderPayloadConverter.cs" />
<Compile Include="Storage\StorageServiceClient.cs" /> <Compile Include="Storage\StorageServiceClient.cs" />
<Compile Include="Storage\StorageServiceClientContext.cs" /> <Compile Include="Storage\StorageServiceClientContext.cs" />
<Compile Include="Storage\StorageServiceClientDefinition.cs" /> <Compile Include="Storage\StorageServiceClientDefinition.cs" />

View File

@@ -40,6 +40,7 @@ namespace Openstack
//Converters //Converters
manager.RegisterServiceInstance(typeof(IStorageContainerPayloadConverter), new StorageContainerPayloadConverter()); manager.RegisterServiceInstance(typeof(IStorageContainerPayloadConverter), new StorageContainerPayloadConverter());
manager.RegisterServiceInstance(typeof(IStorageObjectPayloadConverter), new StorageObjectPayloadConverter()); manager.RegisterServiceInstance(typeof(IStorageObjectPayloadConverter), new StorageObjectPayloadConverter());
manager.RegisterServiceInstance(typeof(IStorageFolderPayloadConverter), new StorageFolderPayloadConverter());
manager.RegisterServiceInstance(typeof(IStorageAccountPayloadConverter), new StorageAccountPayloadConverter()); manager.RegisterServiceInstance(typeof(IStorageAccountPayloadConverter), new StorageAccountPayloadConverter());
manager.RegisterServiceInstance(typeof(IAccessTokenPayloadConverter), new AccessTokenPayloadConverter()); manager.RegisterServiceInstance(typeof(IAccessTokenPayloadConverter), new AccessTokenPayloadConverter());
manager.RegisterServiceInstance(typeof(IOpenstackServiceCatalogPayloadConverter), new OpenstackServiceCatalogPayloadConverter()); manager.RegisterServiceInstance(typeof(IOpenstackServiceCatalogPayloadConverter), new OpenstackServiceCatalogPayloadConverter());

View File

@@ -0,0 +1,25 @@
// /* ============================================================================
// Copyright 2014 Hewlett Packard
//
// 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.
// ============================================================================ */
namespace Openstack.Storage
{
using System.Collections.Generic;
public interface IStorageFolderPayloadConverter
{
IEnumerable<StorageFolder> Convert(IEnumerable<StorageObject> objects);
}
}

View File

@@ -45,6 +45,11 @@ namespace Openstack.Storage
/// </summary> /// </summary>
public IEnumerable<StorageObject> Objects { get; private set; } public IEnumerable<StorageObject> Objects { get; private set; }
/// <summary>
/// Gets a list of storage folders that are in the container.
/// </summary>
public IEnumerable<StorageFolder> Folders { get; private set; }
/// <summary> /// <summary>
/// Gets the metadata associated with the storage container. /// Gets the metadata associated with the storage container.
/// </summary> /// </summary>
@@ -78,18 +83,34 @@ namespace Openstack.Storage
/// <param name="totalObjects">The total number of objects in the container.</param> /// <param name="totalObjects">The total number of objects in the container.</param>
/// <param name="objects">A list of storage objects that are in the container.</param> /// <param name="objects">A list of storage objects that are in the container.</param>
/// <param name="metadata">Metadata associated with the storage container.</param> /// <param name="metadata">Metadata associated with the storage container.</param>
internal StorageContainer(string name, long totalBytes, int totalObjects, IDictionary<string,string> metadata, IEnumerable<StorageObject> objects) internal StorageContainer(string name, long totalBytes, int totalObjects, IDictionary<string,string> metadata, IEnumerable<StorageObject> objects)
: this(name, totalBytes,totalObjects, metadata, objects, new List<StorageFolder>())
{
}
/// <summary>
/// Creates a new instance of the StorageContainer class.
/// </summary>
/// <param name="name">The name of the storage container.</param>
/// <param name="totalBytes">The total number of bytes used in the container.</param>
/// <param name="totalObjects">The total number of objects in the container.</param>
/// <param name="objects">A list of storage objects that are in the container.</param>
/// <param name="folders">A list of storage folders that are in the container.</param>
/// <param name="metadata">Metadata associated with the storage container.</param>
internal StorageContainer(string name, long totalBytes, int totalObjects, IDictionary<string, string> metadata, IEnumerable<StorageObject> objects, IEnumerable<StorageFolder> folders)
{ {
name.AssertIsNotNullOrEmpty("name"); name.AssertIsNotNullOrEmpty("name");
totalBytes.AssertIsNotNull("totalBytes"); totalBytes.AssertIsNotNull("totalBytes");
totalObjects.AssertIsNotNull("totalObjects"); totalObjects.AssertIsNotNull("totalObjects");
metadata.AssertIsNotNull("metadata"); metadata.AssertIsNotNull("metadata");
objects.AssertIsNotNull("objects"); objects.AssertIsNotNull("objects");
folders.AssertIsNotNull("folders");
this.Name = name; this.Name = name;
this.TotalBytesUsed = totalBytes; this.TotalBytesUsed = totalBytes;
this.TotalObjectCount = totalObjects; this.TotalObjectCount = totalObjects;
this.Objects = objects.ToList(); this.Objects = objects.ToList();
this.Folders = folders.ToList();
this.Metadata = metadata; this.Metadata = metadata;
} }
} }

View File

@@ -97,14 +97,17 @@ namespace Openstack.Storage
payload.AssertIsNotNull("payload"); payload.AssertIsNotNull("payload");
var objectConverter = ServiceLocator.Instance.Locate<IStorageObjectPayloadConverter>(); var objectConverter = ServiceLocator.Instance.Locate<IStorageObjectPayloadConverter>();
var folderConverter = ServiceLocator.Instance.Locate<IStorageFolderPayloadConverter>();
try try
{ {
var totalBytes = long.Parse(headers["X-Container-Bytes-Used"].First()); var totalBytes = long.Parse(headers["X-Container-Bytes-Used"].First());
var totalObjects = int.Parse(headers["X-Container-Object-Count"].First()); var totalObjects = int.Parse(headers["X-Container-Object-Count"].First());
var metadata = headers.Where(kvp => kvp.Key.StartsWith("X-Container-Meta")).ToDictionary(header => header.Key.Substring(17, header.Key.Length - 17), header => header.Value.First());
var objects = objectConverter.Convert(name, payload); var objects = objectConverter.Convert(name, payload);
var folders = folderConverter.Convert(objects);
return new StorageContainer(name, totalBytes, totalObjects, objects); return new StorageContainer(name, totalBytes, totalObjects, metadata, objects, folders);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -0,0 +1,56 @@
// /* ============================================================================
// Copyright 2014 Hewlett Packard
//
// 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.
// ============================================================================ */
namespace Openstack.Storage
{
using System.Linq;
using Openstack.Common;
using System.Collections.Generic;
public class StorageFolder
{
public string Name { get; private set; }
public string FullName { get; private set; }
public ICollection<StorageFolder> Folders { get; private set; }
public ICollection<StorageObject> Objects { get; private set; }
public StorageFolder(string fullName, IEnumerable<StorageFolder> folders) : this(fullName, folders, new List<StorageObject>())
{ }
public StorageFolder(string fullName, IEnumerable<StorageFolder> folders, IEnumerable<StorageObject> objects )
{
fullName.AssertIsNotNullOrEmpty("fullName", "Cannot create a storage folder with a null or empty full name.");
folders.AssertIsNotNull("folders", "Cannot create a storage folder with a null folders collection.");
objects.AssertIsNotNull("objects", "Cannot create a storage folder with a null objects collection.");
this.FullName = fullName.Trim('/');
this.Name = ExtractFolderName(this.FullName);
this.Folders = folders.ToList();
this.Objects = objects.ToList();
}
internal static string ExtractFolderName(string fullFolderName)
{
var fullName = fullFolderName.Trim('/');
var lastIndex = fullName.LastIndexOf('/');
lastIndex++;
return fullName.Substring(lastIndex, fullName.Length - lastIndex);
}
}
}

View File

@@ -0,0 +1,88 @@
// /* ============================================================================
// Copyright 2014 Hewlett Packard
//
// 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.
// ============================================================================ */
namespace Openstack.Storage
{
using System;
using System.Linq;
using Openstack.Common;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text.RegularExpressions;
internal class StorageFolderPayloadConverter : IStorageFolderPayloadConverter
{
internal const string consecutiveSlashRegex = @"/{2,}";
public IEnumerable<StorageFolder> Convert(IEnumerable<StorageObject> objects)
{
objects.AssertIsNotNull("objects", "Cannot build folders with a null object collection.");
var folders = new List<StorageFolder>();
var sortedObjectList = objects.OrderByDescending(o => o.Name.Length).ToList();
foreach (var obj in sortedObjectList)
{
//if the name has any consecutive slashes in the name, skip it.
if (Regex.IsMatch(obj.Name, consecutiveSlashRegex))
{
continue;
}
//split the input using a forward slash as the folder delimiter, and separate the object name (if we have one) and the folder path.
var folderParts = obj.Name.TrimStart('/').Split('/');
var objectName = folderParts.Last(); //this will be string.empty if the object name ends in a "/" indicating that it's a folder.
folderParts = folderParts.Take(folderParts.Length - 1).ToArray();
//if there are no folders in the object's name, skip it.
if (folderParts.Count() <= 0)
{
continue;
}
var currentRoot = folders.FirstOrDefault(f => string.Compare(f.Name, folderParts[0], StringComparison.InvariantCulture) == 0);
if (currentRoot == null)
{
//if the root folder does not exist, create it.
currentRoot = new StorageFolder(folderParts[0], new List<StorageFolder>());
folders.Add(currentRoot);
}
//go through the rest of the folder path (if any) and add nested folders (if needed).
var currentPath = folderParts[0];
foreach (var part in folderParts.Skip(1))
{
currentPath += "/" + part;
var newRoot = currentRoot.Folders.FirstOrDefault(f => string.Compare(f.Name, part, StringComparison.InvariantCulture) == 0);
if (newRoot == null)
{
newRoot = new StorageFolder(currentPath, new List<StorageFolder>());
currentRoot.Folders.Add(newRoot);
}
currentRoot = newRoot;
}
//if this object is not a folder (e.g. the name does not end in a /), then add this object to the current folder.
if (!string.IsNullOrEmpty(objectName))
{
currentRoot.Objects.Add(obj);
}
}
return folders;
}
}
}