Adding support for storage manifests at the poco level. Includes basic CRUD operations.

Implements: blueprint large-file-support
Change-Id: I46e05f9692dccad5a2ddfd250f7ba3decba29d20
This commit is contained in:
Wayne Foley 2014-03-31 16:14:40 -07:00
parent b17cddd189
commit 2f91391b7a
25 changed files with 1861 additions and 137 deletions

View File

@ -338,7 +338,7 @@ namespace Openstack.Test.Storage
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"));
Assert.IsTrue(aNode.Objects.Any(f => f.FullName == "a/object2"));
var xNode = aNode.Folders.First(f => f.Name == "x");
Assert.AreEqual(0, xNode.Folders.Count);
@ -348,13 +348,13 @@ namespace Openstack.Test.Storage
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"));
Assert.IsTrue(bNode.Objects.Any(f => f.FullName == "a/b/c"));
Assert.IsTrue(bNode.Objects.Any(f => f.FullName == "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"));
Assert.IsTrue(cNode.Objects.Any(f => f.FullName == "a/b/c/object3"));
}

View File

@ -119,8 +119,8 @@ namespace Openstack.Test.Storage
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"));
Assert.IsTrue(leaf.Objects.Any(o => o.FullName == "a/b/c/d/foo"));
Assert.IsTrue(leaf.Objects.Any(o => o.FullName == "a/b/c/d/bar"));
}
[TestMethod]
@ -142,8 +142,8 @@ namespace Openstack.Test.Storage
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"));
Assert.IsTrue(leaf.Objects.Any(o => o.FullName == "a/b/c/d/foo"));
Assert.IsTrue(leaf.Objects.Any(o => o.FullName == "a/b/c/d/bar"));
}
[TestMethod]
@ -165,8 +165,8 @@ namespace Openstack.Test.Storage
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"));
Assert.IsTrue(leaf.Objects.Any(o => o.FullName == "a/b/c/d/foo"));
Assert.IsTrue(leaf.Objects.Any(o => o.FullName == "a/b/c/d/bar"));
}
[TestMethod]
@ -194,24 +194,24 @@ namespace Openstack.Test.Storage
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.IsTrue(aNode.Objects.Any(o => o.FullName == "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.IsTrue(bNode.Objects.Any(o => o.FullName == "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.IsTrue(cNode.Objects.Any(o => o.FullName == "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"));
Assert.IsTrue(dNode.Objects.Any(o => o.FullName == "a/b/c/d/foo"));
}
[TestMethod]
@ -287,7 +287,7 @@ namespace Openstack.Test.Storage
Assert.AreEqual("c", resp.Name);
var obj = resp.Objects.First();
Assert.AreEqual("a/b/c/BLAH", obj.Name);
Assert.AreEqual("a/b/c/BLAH", obj.FullName);
}
[TestMethod]
@ -332,7 +332,7 @@ namespace Openstack.Test.Storage
Assert.AreEqual("c", resp.Name);
var obj = resp.Objects.First();
Assert.AreEqual("a/b/c/BLAH", obj.Name);
Assert.AreEqual("a/b/c/BLAH", obj.FullName);
}
[TestMethod]
@ -364,7 +364,7 @@ namespace Openstack.Test.Storage
Assert.AreEqual("c", resp.Name);
var obj = resp.Objects.First();
Assert.AreEqual("a/b/c/BLAH", obj.Name);
Assert.AreEqual("a/b/c/BLAH", obj.FullName);
var dNode = resp.Folders.First(f => f.FullName == "a/b/c/d");
var xNode = resp.Folders.First(f => f.FullName == "a/b/c/x");
@ -415,7 +415,7 @@ namespace Openstack.Test.Storage
Assert.AreEqual(2, resp.Folders.Count);
var obj = resp.Objects.First();
Assert.AreEqual("a/b/c/BLAH", obj.Name);
Assert.AreEqual("a/b/c/BLAH", obj.FullName);
var dNode = resp.Folders.First(f => f.FullName == "a/b/c/d");
var xNode = resp.Folders.First(f => f.FullName == "a/b/c/x");

View File

@ -15,9 +15,12 @@
// ============================================================================ */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Openstack.Common.Http;
using Openstack.Storage;
@ -294,7 +297,7 @@ namespace Openstack.Test.Storage
}
[TestMethod]
public void CanParseFromHeaders()
public void CanParseObjectFromHeaders()
{
var containerName = "TestContainer";
var objectName = "TestObject";
@ -311,6 +314,7 @@ namespace Openstack.Test.Storage
var obj = converter.Convert(containerName, objectName, headers);
Assert.IsNotNull(obj);
Assert.IsInstanceOfType(obj, typeof(StorageObject));
Assert.AreEqual(1234, obj.Length);
Assert.AreEqual("d41d8cd98f00b204e9800998ecf8427e", obj.ETag);
Assert.AreEqual("application/octet-stream", obj.ContentType);
@ -320,7 +324,7 @@ namespace Openstack.Test.Storage
}
[TestMethod]
public void CanParseFromHeadersWithMetadata()
public void CanParseObjectFromHeadersWithMetadata()
{
var containerName = "TestContainer";
var objectName = "TestObject";
@ -338,6 +342,7 @@ namespace Openstack.Test.Storage
var obj = converter.Convert(containerName, objectName, headers);
Assert.IsNotNull(obj);
Assert.IsInstanceOfType(obj, typeof(StorageObject));
Assert.AreEqual(1234, obj.Length);
Assert.AreEqual("d41d8cd98f00b204e9800998ecf8427e", obj.ETag);
Assert.AreEqual("application/octet-stream", obj.ContentType);
@ -349,6 +354,200 @@ namespace Openstack.Test.Storage
Assert.AreEqual("Test1", obj.Metadata["Test1"]);
}
[TestMethod]
public void CanParseStaticManifestFromHeadersWithMetadata()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var headers = new HttpHeadersAbstraction()
{
{"Content-Length", "1234"},
{"Content-Type", "application/octet-stream"},
{"Last-Modified", "Wed, 12 Mar 2014 23:42:23 GMT"},
{"ETag", "d41d8cd98f00b204e9800998ecf8427e"},
{"X-Object-Meta-Test1","Test1"},
{"X-Static-Large-Object","True"}
};
var converter = new StorageObjectPayloadConverter();
var obj = converter.Convert(containerName, objectName, headers);
Assert.IsNotNull(obj);
Assert.IsInstanceOfType(obj, typeof(StaticLargeObjectManifest));
Assert.AreEqual(1234, obj.Length);
Assert.AreEqual("d41d8cd98f00b204e9800998ecf8427e", obj.ETag);
Assert.AreEqual("application/octet-stream", obj.ContentType);
Assert.AreEqual(DateTime.Parse("Wed, 12 Mar 2014 23:42:23 GMT"), obj.LastModified);
Assert.AreEqual(objectName, obj.Name);
Assert.AreEqual(containerName, obj.ContainerName);
Assert.AreEqual(1, obj.Metadata.Count());
Assert.IsTrue(obj.Metadata.ContainsKey("Test1"));
Assert.AreEqual("Test1", obj.Metadata["Test1"]);
}
[TestMethod]
public void CanParseObjectFromHeadersWithStaticManifestFlagSetToNonTrue()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var headers = new HttpHeadersAbstraction()
{
{"Content-Length", "1234"},
{"Content-Type", "application/octet-stream"},
{"Last-Modified", "Wed, 12 Mar 2014 23:42:23 GMT"},
{"ETag", "d41d8cd98f00b204e9800998ecf8427e"},
{"X-Object-Meta-Test1","Test1"},
{"X-Static-Large-Object","False"}
};
var converter = new StorageObjectPayloadConverter();
var obj = converter.Convert(containerName, objectName, headers);
Assert.IsNotNull(obj);
Assert.IsInstanceOfType(obj, typeof(StorageObject));
Assert.AreEqual(1234, obj.Length);
Assert.AreEqual("d41d8cd98f00b204e9800998ecf8427e", obj.ETag);
Assert.AreEqual("application/octet-stream", obj.ContentType);
Assert.AreEqual(DateTime.Parse("Wed, 12 Mar 2014 23:42:23 GMT"), obj.LastModified);
Assert.AreEqual(objectName, obj.Name);
Assert.AreEqual(containerName, obj.ContainerName);
Assert.AreEqual(1, obj.Metadata.Count());
Assert.IsTrue(obj.Metadata.ContainsKey("Test1"));
Assert.AreEqual("Test1", obj.Metadata["Test1"]);
}
[TestMethod]
public void CanParseObjectFromHeadersWithStaticManifestFlagSetToNull()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var headers = new HttpHeadersAbstraction()
{
{"Content-Length", "1234"},
{"Content-Type", "application/octet-stream"},
{"Last-Modified", "Wed, 12 Mar 2014 23:42:23 GMT"},
{"ETag", "d41d8cd98f00b204e9800998ecf8427e"},
{"X-Object-Meta-Test1","Test1"},
{"X-Static-Large-Object", (string)null}
};
var converter = new StorageObjectPayloadConverter();
var obj = converter.Convert(containerName, objectName, headers);
Assert.IsNotNull(obj);
Assert.IsInstanceOfType(obj, typeof(StorageObject));
Assert.AreEqual(1234, obj.Length);
Assert.AreEqual("d41d8cd98f00b204e9800998ecf8427e", obj.ETag);
Assert.AreEqual("application/octet-stream", obj.ContentType);
Assert.AreEqual(DateTime.Parse("Wed, 12 Mar 2014 23:42:23 GMT"), obj.LastModified);
Assert.AreEqual(objectName, obj.Name);
Assert.AreEqual(containerName, obj.ContainerName);
Assert.AreEqual(1, obj.Metadata.Count());
Assert.IsTrue(obj.Metadata.ContainsKey("Test1"));
Assert.AreEqual("Test1", obj.Metadata["Test1"]);
}
[TestMethod]
public void CanParseObjectFromHeadersWithDynamicFlagSetToNull()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var headers = new HttpHeadersAbstraction()
{
{"Content-Length", "1234"},
{"Content-Type", "application/octet-stream"},
{"Last-Modified", "Wed, 12 Mar 2014 23:42:23 GMT"},
{"ETag", "d41d8cd98f00b204e9800998ecf8427e"},
{"X-Object-Meta-Test1","Test1"},
{"X-Object-Manifest", (string)null}
};
var converter = new StorageObjectPayloadConverter();
var obj = converter.Convert(containerName, objectName, headers);
Assert.IsNotNull(obj);
Assert.IsInstanceOfType(obj, typeof(StorageObject));
Assert.AreEqual(1234, obj.Length);
Assert.AreEqual("d41d8cd98f00b204e9800998ecf8427e", obj.ETag);
Assert.AreEqual("application/octet-stream", obj.ContentType);
Assert.AreEqual(DateTime.Parse("Wed, 12 Mar 2014 23:42:23 GMT"), obj.LastModified);
Assert.AreEqual(objectName, obj.Name);
Assert.AreEqual(containerName, obj.ContainerName);
Assert.AreEqual(1, obj.Metadata.Count());
Assert.IsTrue(obj.Metadata.ContainsKey("Test1"));
Assert.AreEqual("Test1", obj.Metadata["Test1"]);
}
[TestMethod]
public void CanParseObjectFromHeadersWithDynamicFlagSetToEmpty()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var headers = new HttpHeadersAbstraction()
{
{"Content-Length", "1234"},
{"Content-Type", "application/octet-stream"},
{"Last-Modified", "Wed, 12 Mar 2014 23:42:23 GMT"},
{"ETag", "d41d8cd98f00b204e9800998ecf8427e"},
{"X-Object-Meta-Test1","Test1"},
{"X-Object-Manifest", string.Empty}
};
var converter = new StorageObjectPayloadConverter();
var obj = converter.Convert(containerName, objectName, headers);
Assert.IsNotNull(obj);
Assert.IsInstanceOfType(obj, typeof(StorageObject));
Assert.AreEqual(1234, obj.Length);
Assert.AreEqual("d41d8cd98f00b204e9800998ecf8427e", obj.ETag);
Assert.AreEqual("application/octet-stream", obj.ContentType);
Assert.AreEqual(DateTime.Parse("Wed, 12 Mar 2014 23:42:23 GMT"), obj.LastModified);
Assert.AreEqual(objectName, obj.Name);
Assert.AreEqual(containerName, obj.ContainerName);
Assert.AreEqual(1, obj.Metadata.Count());
Assert.IsTrue(obj.Metadata.ContainsKey("Test1"));
Assert.AreEqual("Test1", obj.Metadata["Test1"]);
}
[TestMethod]
public void CanParseDynamicManifestFromHeadersWithMetadata()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var headers = new HttpHeadersAbstraction()
{
{"Content-Length", "1234"},
{"Content-Type", "application/octet-stream"},
{"Last-Modified", "Wed, 12 Mar 2014 23:42:23 GMT"},
{"ETag", "d41d8cd98f00b204e9800998ecf8427e"},
{"X-Object-Meta-Test1","Test1"},
{"X-Object-Manifest","a/b"}
};
var converter = new StorageObjectPayloadConverter();
var obj = converter.Convert(containerName, objectName, headers);
Assert.IsNotNull(obj);
Assert.IsInstanceOfType(obj, typeof(DynamicLargeObjectManifest));
Assert.AreEqual(1234, obj.Length);
Assert.AreEqual("d41d8cd98f00b204e9800998ecf8427e", obj.ETag);
Assert.AreEqual("application/octet-stream", obj.ContentType);
Assert.AreEqual(DateTime.Parse("Wed, 12 Mar 2014 23:42:23 GMT"), obj.LastModified);
Assert.AreEqual(objectName, obj.Name);
Assert.AreEqual(containerName, obj.ContainerName);
Assert.AreEqual(1, obj.Metadata.Count());
Assert.IsTrue(obj.Metadata.ContainsKey("Test1"));
Assert.AreEqual("Test1", obj.Metadata["Test1"]);
var manifest = obj as DynamicLargeObjectManifest;
Assert.AreEqual("a/b", manifest.SegmentsPath);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
@ -544,5 +743,66 @@ namespace Openstack.Test.Storage
var converter = new StorageObjectPayloadConverter();
converter.Convert(containerName, objectName, headers);
}
[TestMethod]
public void CanConvertSingleStorageObjectToJson()
{
var obj = new StorageObject("a/b/c", "TestContainer", DateTime.UtcNow, "12345", 54321, string.Empty,
new Dictionary<string, string>());
var converter = new StorageObjectPayloadConverter();
var payload = converter.Convert(new List<StorageObject>() { obj});
var result = JArray.Parse(payload);
Assert.AreEqual(1,result.Count);
var item = result[0];
Assert.AreEqual("TestContainer/a/b/c", item["path"]);
Assert.AreEqual(54321, item["size_bytes"]);
Assert.AreEqual("12345", item["etag"]);
}
[TestMethod]
public void CanConvertMultipleStorageObjectToJson()
{
var obj = new StorageObject("a/b/c", "TestContainer", DateTime.UtcNow, "12345", 54321, string.Empty,
new Dictionary<string, string>());
var obj2 = new StorageObject("a/b/d", "TestContainer", DateTime.UtcNow, "00000", 11111, string.Empty,
new Dictionary<string, string>());
var converter = new StorageObjectPayloadConverter();
var payload = converter.Convert(new List<StorageObject>() { obj, obj2 });
var result = JArray.Parse(payload);
Assert.AreEqual(2, result.Count);
var item = result[0];
Assert.AreEqual("TestContainer/a/b/c", item["path"]);
Assert.AreEqual(54321, item["size_bytes"]);
Assert.AreEqual("12345", item["etag"]);
var item2 = result[1];
Assert.AreEqual("TestContainer/a/b/d", item2["path"]);
Assert.AreEqual(11111, item2["size_bytes"]);
Assert.AreEqual("00000", item2["etag"]);
}
[TestMethod]
public void CanConvertEmptyStorageObjectsToJson()
{
var converter = new StorageObjectPayloadConverter();
var payload = converter.Convert(new List<StorageObject>());
var result = JArray.Parse(payload);
Assert.AreEqual(0, result.Count);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void CannotConvertNullStorageObjectsToJson()
{
var converter = new StorageObjectPayloadConverter();
converter.Convert(null);
}
}
}

View File

@ -386,6 +386,200 @@ namespace Openstack.Test.Storage
await client.CreateStorageObject(containerName, objectName, null, new MemoryStream());
}
[TestMethod]
public async Task CanCreateStaticStorageManifest()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var obj = new StorageObject(objectName, containerName, DateTime.UtcNow, "12345", 12345,
"application/octet-stream", new Dictionary<string, string>());
this.ServicePocoClient.CreateStorageManifestDelegate = async (m) =>
{
Assert.IsInstanceOfType(m,typeof(StaticLargeObjectManifest));
return await Task.Run(() => m);
};
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, objectName, new Dictionary<string, string>(), new List<StorageObject>() { obj });
}
[TestMethod]
public async Task CanCreateDynamicStorageManifest()
{
var containerName = "TestContainer";
var objectName = "TestObject";
this.ServicePocoClient.CreateStorageManifestDelegate = async (m) =>
{
Assert.IsInstanceOfType(m, typeof(DynamicLargeObjectManifest));
return await Task.Run(() => m);
};
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, objectName, new Dictionary<string, string>(), "segments");
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public async Task CreatingDynamicStorageManifestWithNullContainerNameThrows()
{
var manifestName = "TestManifest";
var segmentPath = "segments";
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(null, manifestName, new Dictionary<string, string>(), segmentPath);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public async Task CreatingDynamicStorageManifestWithEmptyContainerNameThrows()
{
var manifestName = "TestManifest";
var segmentPath = "segments";
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(string.Empty, manifestName, new Dictionary<string, string>(), segmentPath);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public async Task CreatingDynamicStorageManifestWithNullManifestNameThrows()
{
var containerName = "TestContainer";
var segmentPath = "segments";
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, null, new Dictionary<string, string>(), segmentPath);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public async Task CreatingDynamicStorageManifestWithEmptyManifestNameThrows()
{
var containerName = "TestContainer";
var segmentPath = "segments";
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, string.Empty, new Dictionary<string, string>(), segmentPath);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public async Task CreatingDynamicStorageManifestWithNullSegmentPathThrows()
{
var containerName = "TestContainer";
var manifestName = "TestManifest";
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, manifestName, new Dictionary<string, string>(), (string)null);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public async Task CreatingDynamicStorageManifestWithEmptySegmentPathThrows()
{
var containerName = "TestContainer";
var manifestName = "TestManifest";
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, manifestName, new Dictionary<string, string>(), string.Empty);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public async Task CreatingDynamicStorageManifestWithNullMetadataThrows()
{
var containerName = "TestContainer";
var manifestName = "TestManifest";
var segmentPath = "segments";
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, manifestName, null, segmentPath);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public async Task CreatingStaticStorageManifestWithNullContainerNameThrows()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var obj = new StorageObject(objectName, containerName, DateTime.UtcNow, "12345", 12345,
"application/octet-stream", new Dictionary<string, string>());
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(null, objectName, new Dictionary<string, string>(), new List<StorageObject>() { obj });
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public async Task CreatingStaticStorageManifestWithEmptyContainerNameThrows()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var obj = new StorageObject(objectName, containerName, DateTime.UtcNow, "12345", 12345,
"application/octet-stream", new Dictionary<string, string>());
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(string.Empty, objectName, new Dictionary<string, string>(), new List<StorageObject>() { obj });
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public async Task CreatingStaticStorageManifestWithNullManifestNameThrows()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var obj = new StorageObject(objectName, containerName, DateTime.UtcNow, "12345", 12345,
"application/octet-stream", new Dictionary<string, string>());
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, null, new Dictionary<string, string>(), new List<StorageObject>() { obj });
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public async Task CreatingStaticStorageManifestWithEmptyManifestNameThrows()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var obj = new StorageObject(objectName, containerName, DateTime.UtcNow, "12345", 12345,
"application/octet-stream", new Dictionary<string, string>());
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, string.Empty, new Dictionary<string, string>(), new List<StorageObject>() { obj });
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public async Task CreatingStaticStorageManifestWithNullObjectListThrows()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, objectName, new Dictionary<string, string>(), (List<StorageObject>)null);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public async Task CreatingStaticStorageManifestWithNullMetadataThrows()
{
var containerName = "TestContainer";
var objectName = "TestObject";
var obj = new StorageObject(objectName, containerName, DateTime.UtcNow, "12345", 12345,
"application/octet-stream", new Dictionary<string, string>());
var client = new StorageServiceClient(GetValidCreds(), CancellationToken.None);
await client.CreateStorageManifest(containerName, objectName, null, new List<StorageObject>() { obj });
}
[TestMethod]
public async Task CanCreateStorageFolder()
{

View File

@ -74,6 +74,76 @@ namespace Openstack.Test.Storage
return new StorageServiceClientContext(creds, token, "Object Storage");
}
#region CreateManifest Tests
[TestMethod]
public async Task CreateStaticStorageManifestIncludesAuthHeader()
{
var manifestName = "NewManifest";
var containerName = "newContainer";
var client =
new StorageServiceRestClient(GetValidContext());
var data = "Some random data";
var content = TestHelper.CreateStream(data);
await client.CreateStaticManifest(containerName, manifestName, new Dictionary<string, string>(), content);
Assert.IsTrue(this.simulator.Headers.ContainsKey("X-Auth-Token"));
Assert.AreEqual(this.authId, this.simulator.Headers["X-Auth-Token"]);
}
[TestMethod]
public async Task CreateDynamicStorageManifestIncludesAuthHeader()
{
var manifestName = "NewManifest";
var segPath = "blah/blah";
var containerName = "newContainer";
var client = new StorageServiceRestClient(GetValidContext());
await client.CreateDynamicManifest(containerName, manifestName, new Dictionary<string, string>(), segPath);
Assert.IsTrue(this.simulator.Headers.ContainsKey("X-Auth-Token"));
Assert.AreEqual(this.authId, this.simulator.Headers["X-Auth-Token"]);
}
[TestMethod]
public async Task CreateStaticManifestFormsCorrectUrlAndMethod()
{
var manifestName = "NewManifest";
var containerName = "newContainer";
var client =
new StorageServiceRestClient(GetValidContext());
var data = "Some random data";
var content = TestHelper.CreateStream(data);
await client.CreateStaticManifest(containerName, manifestName, new Dictionary<string, string>(), content);
Assert.AreEqual(string.Format("{0}/{1}/{2}?multipart-manifest=put", endpoint, containerName, manifestName), this.simulator.Uri.ToString());
Assert.AreEqual(HttpMethod.Put, this.simulator.Method);
}
[TestMethod]
public async Task CreateDynamicManifestFormsCorrectUrlMethodAndHeader()
{
var manifestName = "NewManifest";
var segPath = "blah/blah";
var containerName = "newContainer";
var client = new StorageServiceRestClient(GetValidContext());
await client.CreateDynamicManifest(containerName, manifestName, new Dictionary<string, string>(), segPath);
Assert.AreEqual(string.Format("{0}/{1}/{2}", endpoint, containerName, manifestName), this.simulator.Uri.ToString());
Assert.AreEqual(HttpMethod.Put, this.simulator.Method);
Assert.IsTrue(this.simulator.Headers.ContainsKey("X-Object-Manifest"));
Assert.AreEqual(segPath, this.simulator.Headers["X-Object-Manifest"]);
}
#endregion
#region CreateObject Tests
[TestMethod]
@ -1381,5 +1451,96 @@ namespace Openstack.Test.Storage
}
#endregion
#region Get Storage Manifest Tests
[TestMethod]
public async Task GetStorageManifestIncludesAuthHeader()
{
var containerName = "newContainer";
var manifestName = "newManifest";
var client =
new StorageServiceRestClient(GetValidContext());
await client.GetManifestMetadata(containerName, manifestName);
Assert.IsTrue(this.simulator.Headers.ContainsKey("X-Auth-Token"));
Assert.AreEqual(this.authId, this.simulator.Headers["X-Auth-Token"]);
}
[TestMethod]
public async Task GetStorageManifestFormsCorrectUrlAndMethod()
{
var containerName = "newContainer";
var manifestName = "a/b/b/manifest";
var client =
new StorageServiceRestClient(GetValidContext());
await client.GetManifestMetadata(containerName, manifestName);
Assert.AreEqual(string.Format("{0}/{1}/{2}?multipart-manifest=get", endpoint, containerName, manifestName), this.simulator.Uri.ToString());
Assert.AreEqual(HttpMethod.Get, this.simulator.Method);
}
[TestMethod]
public async Task ErrorIsReturnedWhenManifestIsNotFound()
{
var containerName = "newContainer";
var manifestName = "a/b/b/manifest";
var client =
new StorageServiceRestClient(GetValidContext());
var resp = await client.GetManifestMetadata(containerName, manifestName);
Assert.AreEqual(HttpStatusCode.NotFound, resp.StatusCode);
}
[TestMethod]
public async Task CanGetStorageManifest()
{
var containerName = "newContainer";
var manifestName = "a/b/b/manifest";
var content = TestHelper.CreateStream("[]");
content.Position = 0;
this.simulator.Objects.Add(manifestName, new StorageRestSimulator.StorageItem(manifestName) { Content = content });
var client =
new StorageServiceRestClient(GetValidContext());
var resp = await client.GetManifestMetadata(containerName, manifestName);
Assert.AreEqual(HttpStatusCode.OK, resp.StatusCode);
}
[TestMethod]
public async Task CanGetStorageManifestWithMetadata()
{
var containerName = "newContainer";
var manifestName = "a/b/b/manifest";
var content = TestHelper.CreateStream(string.Empty);
content.Position = 0;
var metaData = new Dictionary<string, string> { { "X-Object-Meta-Test1", "Test1" }, { "X-Object-Meta-Test2", "Test2" } };
this.simulator.Objects.Add(manifestName, new StorageRestSimulator.StorageItem(manifestName) { MetaData = metaData, Content = content });
var client =
new StorageServiceRestClient(GetValidContext());
var resp = await client.GetManifestMetadata(containerName, manifestName);
Assert.AreEqual(HttpStatusCode.OK, resp.StatusCode);
Assert.IsTrue(resp.Headers.Any(kvp => kvp.Key == "X-Object-Meta-Test2"));
Assert.AreEqual("Test2", resp.Headers.First(kvp => kvp.Key == "X-Object-Meta-Test2").Value.First());
}
#endregion
}
}

View File

@ -25,6 +25,8 @@ namespace Openstack.Test.Storage
{
public Func<StorageObject, Stream, Task<StorageObject>> CreateStorageObjectDelegate { get; set; }
public Func<StorageManifest,Task<StorageManifest>> CreateStorageManifestDelegate { get; set; }
public Func<StorageContainer, Task<StorageContainer>> CreateStorageContainerDelegate { get; set; }
public Func<string, string, Task> CreateStorageFolderDelegate { get; set; }
@ -35,6 +37,8 @@ namespace Openstack.Test.Storage
public Func<string, string, Task<StorageObject>> GetStorageObjectDelegate { get; set; }
public Func<string, string, Task<StorageManifest>> GetStorageManifestDelegate { get; set; }
public Func<string, string, Task<StorageFolder>> GetStorageFolderDelegate { get; set; }
public Func<string, string, Stream, Task<StorageObject>> DownloadStorageObjectDelegate { get; set; }
@ -54,6 +58,11 @@ namespace Openstack.Test.Storage
return await this.CreateStorageObjectDelegate(obj, content);
}
public async Task<StorageManifest> CreateStorageManifest(StorageManifest manifest)
{
return await this.CreateStorageManifestDelegate(manifest);
}
public async Task CreateStorageContainer(StorageContainer container)
{
await this.CreateStorageContainerDelegate(container);
@ -74,14 +83,19 @@ namespace Openstack.Test.Storage
return await this.GetStorageObjectDelegate(containerName, objectName);
}
public async Task<StorageManifest> GetStorageManifest(string containerName, string manifestName)
{
return await this.GetStorageManifestDelegate(containerName, manifestName);
}
public async Task<StorageObject> DownloadStorageObject(string containerName, string objectName, Stream outputStream)
{
return await this.DownloadStorageObjectDelegate(containerName, objectName, outputStream);
}
public async Task DeleteStorageObject(string containerName, string objectName)
public async Task DeleteStorageObject(string containerName, string itemName)
{
await this.DeleteStorageObjectDelegate(containerName, objectName);
await this.DeleteStorageObjectDelegate(containerName, itemName);
}
public async Task DeleteStorageContainer(string containerName)
@ -94,9 +108,9 @@ namespace Openstack.Test.Storage
await this.UpdateStorageContainerDelegate(container);
}
public async Task UpdateStorageObject(StorageObject obj)
public async Task UpdateStorageObject(StorageObject item)
{
await this.UpdateStorageObjectDelegate(obj);
await this.UpdateStorageObjectDelegate(item);
}
public async Task<StorageFolder> GetStorageFolder(string containerName, string folderName)

View File

@ -24,72 +24,97 @@ namespace Openstack.Test.Storage
{
public class TestStorageServiceRestClient : IStorageServiceRestClient
{
public IHttpResponseAbstraction Response { get; set; }
public bool CreateStaticManifestCalled = false;
public bool CreatedDynamicManifestCalled = false;
public TestStorageServiceRestClient()
{
this.Responses = new Queue<IHttpResponseAbstraction>();
}
public Queue<IHttpResponseAbstraction> Responses { get; set; }
public Task<IHttpResponseAbstraction> CreateObject(string containerName, string objectName, IDictionary<string, string> metadata, Stream content)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> CreateDynamicManifest(string containerName, string manifestName, IDictionary<string, string> metadata, string segmentsPath)
{
CreatedDynamicManifestCalled = true;
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> CreateStaticManifest(string containerName, string manifestName, IDictionary<string, string> metadata, Stream content)
{
CreateStaticManifestCalled = true;
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> CreateContainer(string containerName, IDictionary<string, string> metadata)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> GetObject(string containerName, string objectName)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> GetFolder(string containerName, string folderName)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> GetContainer(string containerName)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> DeleteObject(string containerName, string objectName)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> DeleteContainer(string containerName)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> UpdateObject(string containerName, string objectName, IDictionary<string, string> metadata)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> UpdateContainer(string containerName, IDictionary<string, string> metadata)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> GetContainerMetadata(string containerName)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> GetObjectMetadata(string containerName, string objectName)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> GetManifestMetadata(string containerName, string manifestName)
{
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> CopyObject(string sourceContainerName, string sourceObjectName, string targetContainerName,
string targetObjectName)
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
public Task<IHttpResponseAbstraction> GetAccount()
{
return Task.Factory.StartNew(() => Response);
return Task.Factory.StartNew(() => Responses.Dequeue());
}
}

View File

@ -88,6 +88,7 @@
<Compile Include="Identity\OpenstackServiceEndpointPayloadConverter.cs" />
<Compile Include="Identity\OpenstackServiceEndpointResolver.cs" />
<Compile Include="ServiceRegistrar.cs" />
<Compile Include="Storage\DynamicLargeObjectManifest.cs" />
<Compile Include="Storage\FolderNameValidator.cs" />
<Compile Include="Storage\ContainerNameValidator.cs" />
<Compile Include="Storage\IStorageAccountPayloadConverter.cs" />
@ -99,8 +100,11 @@
<Compile Include="Storage\IStorageServicePocoClientFactory.cs" />
<Compile Include="Storage\IStorageServiceRestClient.cs" />
<Compile Include="Storage\IStorageServiceRestClientFactory.cs" />
<Compile Include="Storage\StaticLargeObjectManifest.cs" />
<Compile Include="Storage\StorageFolder.cs" />
<Compile Include="Storage\StorageFolderPayloadConverter.cs" />
<Compile Include="Storage\StorageItemNameExtractor.cs" />
<Compile Include="Storage\StorageManifest.cs" />
<Compile Include="Storage\StorageServiceClient.cs" />
<Compile Include="Storage\StorageServiceClientContext.cs" />
<Compile Include="Storage\StorageServiceClientDefinition.cs" />

View File

@ -31,6 +31,7 @@ namespace Openstack
manager.RegisterServiceInstance(typeof(IStorageServiceRestClientFactory), new StorageServiceRestClientFactory());
manager.RegisterServiceInstance(typeof(IStorageContainerNameValidator), new StorageContainerNameValidator());
manager.RegisterServiceInstance(typeof(IStorageFolderNameValidator), new StorageFolderNameValidator());
manager.RegisterServiceInstance(typeof(IStorageItemNameExtractor), new StorageItemNameExtractor());
//Identity related clients/services
manager.RegisterServiceInstance(typeof(IIdentityServicePocoClientFactory), new IdentityServicePocoClientFactory());

View File

@ -0,0 +1,75 @@
// /* ============================================================================
// 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.Collections.Generic;
using Openstack.Common;
/// <summary>
/// Represents the manifest for a dynamic large object on the remote instance of Openstack.
/// </summary>
public class DynamicLargeObjectManifest : StorageManifest
{
/// <summary>
/// Gets the path where the object segments that make up the manifest can be found.
/// </summary>
public string SegmentsPath { get; internal set; }
/// <summary>
/// Creates a new instance of the DynamicLargeObjectManifest class.
/// </summary>
/// <param name="containerName">The name of the parent container.</param>
/// <param name="manifestFullName">The full name of the manifest file</param>
/// <param name="segmentsPath">The path where the segments that make up this manifest can be found.</param>
public DynamicLargeObjectManifest(string containerName, string manifestFullName, string segmentsPath) : this(containerName, manifestFullName, new Dictionary<string, string>(), segmentsPath)
{
}
/// <summary>
/// Creates a new instance of the DynamicLargeObjectManifest class.
/// </summary>
/// <param name="containerName">The name of the parent container.</param>
/// <param name="manifestFullName">The full name of the manifest file</param>
/// <param name="metadata">The metadata associated with the storage manifest.</param>
/// <param name="segmentsPath">The path where the segments that make up this manifest can be found.</param>
public DynamicLargeObjectManifest(string containerName, string manifestFullName, IDictionary<string,string> metadata, string segmentsPath)
: base(containerName, manifestFullName, metadata)
{
segmentsPath.AssertIsNotNullOrEmpty("segmentsPath", "Cannot create a dynamic large object manifest with a null or empty segments path.");
this.SegmentsPath = segmentsPath;
}
/// <summary>
/// Creates a new instance of the DynamicLargeObjectManifest class.
/// </summary>
/// <param name="fullName">The full name of the storage manifest.</param>
/// <param name="containerName">The name of the parent storage container for the storage manifest.</param>
/// <param name="lastModified">The last modified data for the storage manifest.</param>
/// <param name="eTag">The ETag for the storage manifest.</param>
/// <param name="length">The length/size of the storage manifest.</param>
/// <param name="contentType">The content type of the storage manifest.</param>
/// <param name="metadata">The metadata associated with the storage manifest.</param>
/// <param name="segmentsPath">The path where the segments that make up this manifest can be found.</param>
internal DynamicLargeObjectManifest(string fullName, string containerName, DateTime lastModified, string eTag,
long length, string contentType, IDictionary<string, string> metadata, string segmentsPath)
: base(fullName, containerName, lastModified, eTag, length, contentType, metadata)
{
this.SegmentsPath = segmentsPath;
}
}
}

View File

@ -42,6 +42,13 @@ namespace Openstack.Storage
/// <returns>The storage object.</returns>
StorageObject Convert(string containerName, string objectName, IHttpHeadersAbstraction headers);
/// <summary>
/// Converts a collection of StorageObjects into a Json payload.
/// </summary>
/// <param name="objects">A collection of StorageObjects to convert.</param>
/// <returns>The Json payload.</returns>
string Convert(IEnumerable<StorageObject> objects);
/// <summary>
/// Converts a Json token into a storage object.
/// </summary>

View File

@ -122,6 +122,26 @@ namespace Openstack.Storage
/// <returns>An async task.</returns>
Task UpdateStorageObject(StorageObject obj);
/// <summary>
/// Creates a storage manifest on the remote Openstack instance.
/// </summary>
/// <param name="containerName">The name of the parent container.</param>
/// <param name="manifestName">The name of the manifest.</param>
/// <param name="metadata">Metadata for the manifest.</param>
/// <param name="objects">The list of storage objects.</param>
/// <returns>An async task.</returns>
Task<StorageManifest> CreateStorageManifest(string containerName, string manifestName, IDictionary<string, string> metadata, IEnumerable<StorageObject> objects);
/// <summary>
/// Creates a storage manifest on the remote Openstack instance.
/// </summary>
/// <param name="containerName">The name of the parent container.</param>
/// <param name="manifestName">The name of the manifest.</param>
/// <param name="metadata">Metadata for the manifest.</param>
/// <param name="segmentsPath">The path to the segment objects in the manifest.</param>
/// <returns>An async task.</returns>
Task<StorageManifest> CreateStorageManifest(string containerName, string manifestName, IDictionary<string, string> metadata, string segmentsPath);
/// <summary>
/// Gets a storage folder from the remote Openstack instance. The returned folder is a shallow object graph representation.
/// </summary>

View File

@ -33,6 +33,13 @@ namespace Openstack.Storage
/// <returns>A storage object.</returns>
Task<StorageObject> CreateStorageObject(StorageObject obj, Stream content);
/// <summary>
/// Creates a storage manifest on the remote Openstack instance.
/// </summary>
/// <param name="manifest">The storage manifest to create.</param>
/// <returns>A storage manifest.</returns>
Task<StorageManifest> CreateStorageManifest(StorageManifest manifest);
/// <summary>
/// Creates a storage container on the remote Openstack instance.
/// </summary>
@ -61,6 +68,14 @@ namespace Openstack.Storage
/// <returns>The storage object.</returns>
Task<StorageObject> GetStorageObject(string containerName, string objectName);
/// <summary>
/// Gets a storage manifest from the remote Openstack instance.
/// </summary>
/// <param name="containerName">The name of the parent storage container.</param>
/// <param name="manifestName">The name of the storage manifest.</param>
/// <returns>The storage manifest.</returns>
Task<StorageManifest> GetStorageManifest(string containerName, string manifestName);
/// <summary>
/// Downloads the content of a storage object from the remote Openstack instance.
/// </summary>
@ -74,7 +89,7 @@ namespace Openstack.Storage
/// Deletes a storage object from the remote Openstack instance.
/// </summary>
/// <param name="containerName">The name of the parent storage container.</param>
/// <param name="objectName">The name of the storage object.</param>
/// <param name="objectName">The name of the object to delete.</param>
/// <returns>An async task.</returns>
Task DeleteStorageObject(string containerName, string objectName);

View File

@ -36,6 +36,26 @@ namespace Openstack.Storage
/// <returns>The Http response from the remote service.</returns>
Task<IHttpResponseAbstraction> CreateObject(string containerName, string objectName, IDictionary<string, string> metadata, Stream content);
/// <summary>
/// Creates a dynamic large object manifest on the remote Openstack instance.
/// </summary>
/// <param name="containerName">The name of the parent storage container.</param>
/// <param name="manifestName">The name of the storage manifest.</param>
/// <param name="metadata">Metadata associated with the storage manifest.</param>
/// <param name="segmentsPath">The path to the segment objects that the manifest points to.</param>
/// <returns>The Http response from the remote service.</returns>
Task<IHttpResponseAbstraction> CreateDynamicManifest(string containerName, string manifestName, IDictionary<string, string> metadata, string segmentsPath);
/// <summary>
/// Creates a static large object manifest on the remote Openstack instance.
/// </summary>
/// <param name="containerName">The name of the parent storage container.</param>
/// <param name="manifestName">The name of the storage manifest.</param>
/// <param name="metadata">Metadata associated with the storage manifest.</param>
/// <param name="content">The manifests content.</param>
/// <returns>The Http response from the remote service.</returns>
Task<IHttpResponseAbstraction> CreateStaticManifest(string containerName, string manifestName, IDictionary<string, string> metadata, Stream content);
/// <summary>
/// Creates a storage container on the remote Openstack instance.
/// </summary>
@ -114,6 +134,14 @@ namespace Openstack.Storage
/// <returns>The Http response from the remote service.</returns>
Task<IHttpResponseAbstraction> GetObjectMetadata(string containerName, string objectName);
/// <summary>
/// Gets the metadata for a storage manifest from the remote Openstack instance.
/// </summary>
/// <param name="containerName">The name of the parent storage container.</param>
/// <param name="manifestName">The name of the storage manifest.</param>
/// <returns>The Http response from the remote service.</returns>
Task<IHttpResponseAbstraction> GetManifestMetadata(string containerName, string manifestName);
/// <summary>
/// Copies an object on the remote Openstack instance.
/// </summary>

View File

@ -0,0 +1,76 @@
// /* ============================================================================
// 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.Collections.Generic;
using Openstack.Common;
/// <summary>
/// Represents a manifest for a static large object on the remote Openstack instance.
/// </summary>
public class StaticLargeObjectManifest : StorageManifest
{
/// <summary>
/// Gets a collection of objects in the manifest.
/// </summary>
public ICollection<StorageObject> Objects { get; internal set; }
/// <summary>
/// Creates a new instance of the StaticLargeObjectManifest class.
/// </summary>
/// <param name="containerName">The name of the parent container.</param>
/// <param name="manifestFullName">The full name of the manifest.</param>
/// <param name="objects">A collection of objects that are included in the manifest.</param>
public StaticLargeObjectManifest(string containerName, string manifestFullName, ICollection<StorageObject> objects ) : base(containerName, manifestFullName)
{
objects.AssertIsNotNull("objects","Cannot create a static large object manifest with a null object collection.");
this.Objects = objects;
}
/// <summary>
/// Creates a new instance of the StaticLargeObjectManifest class.
/// </summary>
/// <param name="containerName">The name of the parent container.</param>
/// <param name="manifestFullName">The full name of the manifest.</param>
/// <param name="objects">A collection of objects that are included in the manifest.</param>
/// <param name="metadata">The metadata associated with the storage manifest.</param>
public StaticLargeObjectManifest(string containerName, string manifestFullName, IDictionary<string, string> metadata, ICollection<StorageObject> objects)
: base(containerName, manifestFullName, metadata)
{
objects.AssertIsNotNull("objects", "Cannot create a static large object manifest with a null object collection.");
this.Objects = objects;
}
/// <summary>
/// Creates a new instance of the StaticLargeObjectManifest class.
/// </summary>
/// <param name="fullName">The full name of the storage manifest.</param>
/// <param name="containerName">The name of the parent storage container for the storage manifest.</param>
/// <param name="lastModified">The last modified data for the storage manifest.</param>
/// <param name="eTag">The ETag for the storage manifest.</param>
/// <param name="length">The length/size of the storage manifest.</param>
/// <param name="contentType">The content type of the storage manifest.</param>
/// <param name="metadata">The metadata associated with the storage manifest.</param>
internal StaticLargeObjectManifest(string fullName, string containerName, DateTime lastModified, string eTag,
long length, string contentType, IDictionary<string, string> metadata)
: base(fullName, containerName, lastModified, eTag, length, contentType, metadata)
{
this.Objects = new List<StorageObject>();
}
}
}

View File

@ -19,6 +19,7 @@ namespace Openstack.Storage
using System.Linq;
using Openstack.Common;
using System.Collections.Generic;
using Openstack.Common.ServiceLocation;
/// <summary>
/// Represents a storage folder on a remote Openstack instance.
@ -78,10 +79,8 @@ namespace Openstack.Storage
/// <returns>The "friendly" name of the folder. (e.g. "b" if the folder path is "a/b/")</returns>
internal static string ExtractFolderName(string fullFolderName)
{
var fullName = fullFolderName.Trim('/');
var lastIndex = fullName.LastIndexOf('/');
lastIndex++;
return fullName.Substring(lastIndex, fullName.Length - lastIndex);
var extractor = ServiceLocator.Instance.Locate<IStorageItemNameExtractor>();
return extractor.ExtractName(fullFolderName);
}
}
}

View File

@ -44,13 +44,13 @@ namespace Openstack.Storage
foreach (var obj in sortedObjectList)
{
//if the name has any consecutive slashes in the name, skip it.
if (Regex.IsMatch(obj.Name, consecutiveSlashRegex))
if (Regex.IsMatch(obj.FullName, 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 folderParts = obj.FullName.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();
@ -113,7 +113,7 @@ namespace Openstack.Storage
var objectConverter = ServiceLocator.Instance.Locate<IStorageObjectPayloadConverter>();
var objects = rawObjects.Select(t => objectConverter.ConvertSingle(t,containerName)).ToList();
objects.RemoveAll(o => string.Compare(o.Name, folderName, StringComparison.InvariantCulture) == 0);
objects.RemoveAll(o => string.Compare(o.FullName, folderName, StringComparison.InvariantCulture) == 0);
return new StorageFolder(folderName, subFolders.Select(ParseSubFolder), objects);

View File

@ -0,0 +1,44 @@
// /* ============================================================================
// 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
{
/// <summary>
/// Extracts the "friendly" name of a storage item.
/// </summary>
public interface IStorageItemNameExtractor
{
/// <summary>
/// Extracts the "friendly" name of a storage item, given the items full name.
/// </summary>
/// <param name="fullName">The items full name.</param>
/// <returns>The items friendly name. (e.g. returns 'b' if the items full name is 'a/b')</returns>
string ExtractName(string fullName);
}
/// <inheritdoc/>
internal class StorageItemNameExtractor : IStorageItemNameExtractor
{
/// <inheritdoc/>
public string ExtractName(string fullItemName)
{
var fullName = fullItemName.Trim('/');
var lastIndex = fullName.LastIndexOf('/');
lastIndex++;
return fullName.Substring(lastIndex, fullName.Length - lastIndex);
}
}
}

View File

@ -0,0 +1,65 @@
// /* ============================================================================
// 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.Collections.Generic;
/// <summary>
/// Base class for storage manifest objects.
/// </summary>
public abstract class StorageManifest : StorageObject
{
/// <summary>
/// Creates a new instance of the StorageManifest class.
/// </summary>
/// <param name="containerName">The name of the parent container.</param>
/// <param name="manifestName">The name of the manifest.</param>
internal StorageManifest(string containerName, string manifestName)
: base(manifestName, containerName)
{
}
/// <summary>
/// Creates a new instance of the StorageManifest class.
/// </summary>
/// <param name="containerName">The name of the parent container.</param>
/// <param name="manifestName">The name of the manifest.</param>
/// <param name="metadata">The related metadata for the manifest.</param>
internal StorageManifest(string containerName, string manifestName,IDictionary<string, string> metadata )
: base(manifestName, containerName, metadata)
{
}
/// <summary>
/// Creates a new instance of the StorageManifest class.
/// </summary>
/// <param name="containerName">The name of the parent container.</param>
/// <param name="fullName">The name of the manifest.</param>
/// <param name="length">The length of the manifest.</param>
/// /// <param name="contentType">The content type of the manifest.</param>
/// <param name="lastModified">The time that the last modification of the manifest was made.</param>
/// <param name="eTag">The eTag for the manifest.</param>
/// <param name="metadata">The related metadata for the manifest.</param>
internal StorageManifest(string fullName, string containerName, DateTime lastModified, string eTag,
long length, string contentType, IDictionary<string, string> metadata)
: base(fullName, containerName, lastModified, eTag, length, contentType, metadata)
{
}
}
}

View File

@ -19,6 +19,7 @@ namespace Openstack.Storage
using System;
using System.Collections.Generic;
using Openstack.Common;
using Openstack.Common.ServiceLocation;
/// <summary>
/// Represents a storage object.
@ -26,37 +27,42 @@ namespace Openstack.Storage
public class StorageObject
{
/// <summary>
/// Gets the name of the storage object.
/// Gets the friendly name of the storage item.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Gets the name of the parent storage container for the storage object.
/// Gets the full name of the storage item.
/// </summary>
public string FullName { get; private set; }
/// <summary>
/// Gets the name of the parent storage container for the storage item.
/// </summary>
public string ContainerName { get; private set; }
/// <summary>
/// Gets the last modified data for the storage object.
/// Gets the last modified data for the storage item.
/// </summary>
public DateTime LastModified { get; private set; }
/// <summary>
/// Gets the ETag for the storage object.
/// Gets the ETag for the storage item.
/// </summary>
public string ETag { get; private set; }
/// <summary>
/// Gets the length/size of the storage object.
/// Gets the length/size of the storage item.
/// </summary>
public long Length { get; private set; }
/// <summary>
/// Gets the content type of the storage object.
/// Gets the content type of the storage item.
/// </summary>
public string ContentType { get; private set; }
/// <summary>
/// Gets the metadata associated with the storage object.
/// Gets the metadata associated with the storage item.
/// </summary>
public IDictionary<string, string> Metadata { get; private set; }
@ -70,7 +76,6 @@ namespace Openstack.Storage
public StorageObject(string name, string containerName, string contentType, IDictionary<string, string> metadata)
: this(name, containerName, DateTime.UtcNow, string.Empty, 0, contentType, metadata)
{
}
/// <summary>
@ -81,7 +86,17 @@ namespace Openstack.Storage
public StorageObject(string name, string containerName)
: this(name, containerName, DateTime.UtcNow, string.Empty, 0, string.Empty, new Dictionary<string, string>())
{
}
/// <summary>
/// Creates a new instance of the StorageObject class.
/// </summary>
/// <param name="name">The name of the storage object.</param>
/// <param name="containerName">The name of the parent storage container for the storage object.</param>
/// <param name="metadata">The metadata associated with the storage object.</param>
public StorageObject(string name, string containerName, IDictionary<string, string> metadata )
: this(name, containerName, DateTime.UtcNow, string.Empty, 0, string.Empty, metadata)
{
}
/// <summary>
@ -101,16 +116,16 @@ namespace Openstack.Storage
/// <summary>
/// Creates a new instance of the StorageObject class.
/// </summary>
/// <param name="name">The name of the storage object.</param>
/// <param name="fullName">The full name of the storage object.</param>
/// <param name="containerName">The name of the parent storage container for the storage object.</param>
/// <param name="lastModified">The last modified data for the storage object.</param>
/// <param name="eTag">The ETag for the storage object.</param>
/// <param name="length">The length/size of the storage object.</param>
/// <param name="contentType">The content type of the storage object.</param>
/// <param name="metadata">The metadata associated with the storage object.</param>
internal StorageObject(string name, string containerName, DateTime lastModified, string eTag, long length, string contentType, IDictionary<string, string> metadata )
internal StorageObject(string fullName, string containerName, DateTime lastModified, string eTag, long length, string contentType, IDictionary<string, string> metadata)
{
name.AssertIsNotNullOrEmpty("name");
fullName.AssertIsNotNullOrEmpty("fullName");
containerName.AssertIsNotNullOrEmpty("containerName");
lastModified.AssertIsNotNull("lastModified");
eTag.AssertIsNotNull("eTag");
@ -118,7 +133,8 @@ namespace Openstack.Storage
contentType.AssertIsNotNull("contentType");
metadata.AssertIsNotNull("metadata");
this.Name = name;
this.FullName = fullName;
this.Name = ExtractName(fullName);
this.ContainerName = containerName;
this.LastModified = lastModified;
this.ETag = eTag;
@ -126,5 +142,16 @@ namespace Openstack.Storage
this.ContentType = contentType;
this.Metadata = metadata;
}
/// <summary>
/// Extracts the "friendly" name from the objects full name.
/// </summary>
/// <param name="fullItemName">The full name of the object.</param>
/// <returns>The "friendly" name of the object. (e.g. "b" if the item path is "a/b")</returns>
internal static string ExtractName(string fullItemName)
{
var extractor = ServiceLocator.Instance.Locate<IStorageItemNameExtractor>();
return extractor.ExtractName(fullItemName);
}
}
}

View File

@ -56,6 +56,25 @@ namespace Openstack.Storage
return objects;
}
/// <inheritdoc/>
public string Convert(IEnumerable<StorageObject> objects)
{
objects.AssertIsNotNull("objects","Cannot convert a null storage object collection to Json.");
var objectsPayload = new JArray();
foreach (var obj in objects)
{
dynamic item = new System.Dynamic.ExpandoObject();
item.path = string.Format("{0}/{1}",obj.ContainerName, obj.FullName);
item.size_bytes = obj.Length;
item.etag = obj.ETag;
objectsPayload.Add(JToken.FromObject(item));
}
return objectsPayload.ToString();
}
/// <inheritdoc/>
public StorageObject ConvertSingle(JToken obj, string containerName)
{
@ -101,12 +120,47 @@ namespace Openstack.Storage
var contentType = headers["Content-Type"].First();
var metadata = headers.Where(kvp => kvp.Key.StartsWith("X-Object-Meta")).ToDictionary(header => header.Key.Substring(14, header.Key.Length - 14), header => header.Value.First());
return new StorageObject(objectName, containerName, lastModified, eTag, length, contentType, metadata);
return CreateStorageObject(objectName, containerName, lastModified, eTag, length, contentType, metadata, headers);
}
catch (Exception ex)
{
throw new HttpParseException(string.Format("Storage object '{0}' could not be parsed.", objectName), ex);
}
}
/// <summary>
/// Creates the appropriate storage object based on the given parameters.
/// </summary>
/// <param name="objectName">the name of the object.</param>
/// <param name="containerName">The name of the parent container.</param>
/// <param name="lastModified">The last modified date of the object.</param>
/// <param name="eTag">The eTag for the object.</param>
/// <param name="length">The length of the object.</param>
/// <param name="contentType">The content type of the object.</param>
/// <param name="metadata">The associated metadata for the object.</param>
/// <param name="headers">The http headers from the object request.</param>
/// <returns>An instance of the StorageObject class.</returns>
internal StorageObject CreateStorageObject(string objectName, string containerName, DateTime lastModified, string eTag, long length, string contentType, IDictionary<string, string> metadata, IHttpHeadersAbstraction headers)
{
IEnumerable<string> values;
if (headers.TryGetValue("X-Static-Large-Object", out values))
{
if (values.Any(v => string.Compare(v, "true", StringComparison.InvariantCultureIgnoreCase) == 0))
{
return new StaticLargeObjectManifest(objectName, containerName, lastModified, eTag, length, contentType, metadata);
}
}
if (headers.TryGetValue("X-Object-Manifest", out values))
{
var segPath = values.FirstOrDefault(v => !string.IsNullOrEmpty(v));
if (!string.IsNullOrEmpty(segPath))
{
return new DynamicLargeObjectManifest(objectName, containerName, lastModified, eTag, length, contentType, metadata, segPath);
}
}
return new StorageObject(objectName, containerName, lastModified, eTag, length, contentType, metadata);
}
}
}

View File

@ -121,6 +121,35 @@ namespace Openstack.Storage
await client.UpdateStorageObject(obj);
}
/// <inheritdoc/>
public async Task<StorageManifest> CreateStorageManifest(string containerName, string manifestName, IDictionary<string, string> metadata, IEnumerable<StorageObject> objects)
{
containerName.AssertIsNotNullOrEmpty("containerName", "Cannot create a storage manifest with a container name that is null or empty.");
manifestName.AssertIsNotNullOrEmpty("manifestNamee", "Cannot create a storage manifest with a name that is null or empty.");
metadata.AssertIsNotNull("metadata","Cannot create a storage manifest with null metadata.");
var client = this.GetPocoClient();
var manifest = new StaticLargeObjectManifest(containerName, manifestName, metadata, objects.ToList());
return await client.CreateStorageManifest(manifest);
}
/// <inheritdoc/>
public async Task<StorageManifest> CreateStorageManifest(string containerName, string manifestName, IDictionary<string, string> metadata, string segmentsPath)
{
containerName.AssertIsNotNullOrEmpty("containerName", "Cannot create a storage manifest with a container name that is null or empty.");
manifestName.AssertIsNotNullOrEmpty("manifestNamee", "Cannot create a storage manifest with a name that is null or empty.");
metadata.AssertIsNotNull("metadata", "Cannot create a storage manifest with null metadata.");
segmentsPath.AssertIsNotNullOrEmpty("segmentsPath", "Cannot create storage manifest with a null or empty segments path.");
var client = this.GetPocoClient();
var manifest = new DynamicLargeObjectManifest(containerName, manifestName, metadata, segmentsPath);
return await client.CreateStorageManifest(manifest);
}
/// <inheritdoc/>
public async Task<StorageFolder> GetStorageFolder(string containerName, string folderName)
{
@ -200,7 +229,7 @@ namespace Openstack.Storage
var container = await client.GetStorageContainer(containerName);
foreach (var storageObject in container.Objects)
{
objects.Add(await client.GetStorageObject(containerName, storageObject.Name));
objects.Add(await client.GetStorageObject(containerName, storageObject.FullName));
}
return objects;

View File

@ -24,7 +24,7 @@ namespace Openstack.Storage
using System.Collections.Generic;
using Openstack.Common;
using Openstack.Common.ServiceLocation;
using Openstack.Common.Http;
/// <inheritdoc/>
internal class StorageServicePocoClient : IStorageServicePocoClient
@ -43,12 +43,12 @@ namespace Openstack.Storage
/// <inheritdoc/>
public async Task<StorageObject> CreateStorageObject(StorageObject obj, Stream content)
{
obj.ContainerName.AssertIsNotNullOrEmpty("containerName", "Cannot create a storage object with a null or empty container name.");
obj.AssertIsNotNull("obj","Cannot Create a null storage object.");
obj.AssertIsNotNull("obj", "Cannot create a null storage object.");
obj.ContainerName.AssertIsNotNullOrEmpty("obj.ContainerName", "Cannot create a storage object with a null or empty container name.");
obj.Name.AssertIsNotNullOrEmpty("obj.Name","Cannot create a storage object without a name.");
var client = this.GetRestClient();
var resp = await client.CreateObject(obj.ContainerName, obj.Name, obj.Metadata, content);
var resp = await client.CreateObject(obj.ContainerName, obj.FullName, obj.Metadata, content);
if (resp.StatusCode != HttpStatusCode.Created)
{
@ -56,11 +56,48 @@ namespace Openstack.Storage
}
var converter = ServiceLocator.Instance.Locate<IStorageObjectPayloadConverter>();
var respObj = converter.Convert(obj.ContainerName, obj.Name, resp.Headers);
var respObj = converter.Convert(obj.ContainerName, obj.FullName, resp.Headers);
return respObj;
}
public async Task<StorageManifest> CreateStorageManifest(StorageManifest manifest)
{
manifest.AssertIsNotNull("manifest", "Cannot create a null storage manifest.");
manifest.ContainerName.AssertIsNotNullOrEmpty("manifest.ContainerName", "Cannot create a storage manifest with a null or empty container name.");
manifest.Name.AssertIsNotNullOrEmpty("manifest.Name", "Cannot create a storage manifest without a name.");
var client = this.GetRestClient();
IHttpResponseAbstraction resp;
var dynamicManifest = manifest as DynamicLargeObjectManifest;
var staticManifest = manifest as StaticLargeObjectManifest;
if (dynamicManifest == null && staticManifest == null)
{
throw new InvalidOperationException(string.Format("Failed to create storage manifest '{0}'. The given manifest type is not supported: '{1}'.", manifest.Name, manifest.GetType().Name));
}
if (dynamicManifest != null) //dynamic large object manifest
{
resp = await client.CreateDynamicManifest(dynamicManifest.ContainerName, dynamicManifest.FullName, dynamicManifest.Metadata, dynamicManifest.SegmentsPath);
}
else //static large object manifest
{
var converter = ServiceLocator.Instance.Locate<IStorageObjectPayloadConverter>();
var manifestPayload = converter.Convert(staticManifest.Objects).ConvertToStream();
resp = await client.CreateStaticManifest(staticManifest.ContainerName, staticManifest.FullName, staticManifest.Metadata, manifestPayload);
}
if (resp.StatusCode != HttpStatusCode.Created)
{
throw new InvalidOperationException(string.Format("Failed to create storage manifest '{0}'. The remote server returned the following status code: '{1}'.", manifest.Name, resp.StatusCode));
}
return await this.GetStorageManifest(manifest.ContainerName, manifest.FullName);
}
/// <inheritdoc/>
public async Task CreateStorageContainer(StorageContainer container)
{
@ -132,9 +169,45 @@ namespace Openstack.Storage
var converter = ServiceLocator.Instance.Locate<IStorageObjectPayloadConverter>();
var obj = converter.Convert(containerName, objectName, resp.Headers);
//If the request object is actually a manifest object, then make sure we go back out and get the details of the manifest.
if (obj is StorageManifest)
{
return await GetStorageManifest(containerName, objectName);
}
return obj;
}
public async Task<StorageManifest> GetStorageManifest(string containerName, string manifestName)
{
containerName.AssertIsNotNullOrEmpty("containerName", "Cannot get a storage manifest with a container name that is null or empty.");
manifestName.AssertIsNotNullOrEmpty("manifestName", "Cannot get a storage manifest with a name that is null or empty.");
var client = this.GetRestClient();
var resp = await client.GetManifestMetadata(containerName, manifestName);
if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.NoContent)
{
throw new InvalidOperationException(string.Format("Failed to get storage manifest '{0}'. The remote server returned the following status code: '{1}'.", manifestName, resp.StatusCode));
}
var objectConverter = ServiceLocator.Instance.Locate<IStorageObjectPayloadConverter>();
var obj = objectConverter.Convert(containerName, manifestName, resp.Headers);
if (!(obj is StorageManifest))
{
throw new InvalidOperationException(string.Format("Failed to get storage manifest '{0}'. The requested object is not a manifest.", manifestName));
}
if (obj is StaticLargeObjectManifest)
{
var manifest = obj as StaticLargeObjectManifest;
manifest.Objects = objectConverter.Convert(containerName, await resp.ReadContentAsStringAsync()).ToList();
}
return obj as StorageManifest;
}
/// <inheritdoc/>
public async Task<StorageObject> DownloadStorageObject(string containerName, string objectName, Stream outputStream)
{
@ -160,17 +233,17 @@ namespace Openstack.Storage
}
/// <inheritdoc/>
public async Task DeleteStorageObject(string containerName, string objectName)
public async Task DeleteStorageObject(string containerName, string itemName)
{
containerName.AssertIsNotNullOrEmpty("containerName", "Cannot delete a storage object with a container name that is null or empty.");
objectName.AssertIsNotNullOrEmpty("objectName", "Cannot delete a storage object with a name that is null or empty.");
itemName.AssertIsNotNullOrEmpty("objectName", "Cannot delete a storage object with a name that is null or empty.");
var client = this.GetRestClient();
var resp = await client.DeleteObject(containerName, objectName);
var resp = await client.DeleteObject(containerName, itemName);
if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.NoContent)
{
throw new InvalidOperationException(string.Format("Failed to delete storage object '{0}'. The remote server returned the following status code: '{1}'.", objectName, resp.StatusCode));
throw new InvalidOperationException(string.Format("Failed to delete storage object '{0}'. The remote server returned the following status code: '{1}'.", itemName, resp.StatusCode));
}
}
@ -203,17 +276,17 @@ namespace Openstack.Storage
}
/// <inheritdoc/>
public async Task UpdateStorageObject(StorageObject obj)
public async Task UpdateStorageObject(StorageObject item)
{
obj.ContainerName.AssertIsNotNullOrEmpty("containerName", "Cannot update a storage object with a container name that is null or empty.");
obj.Name.AssertIsNotNullOrEmpty("objectName", "Cannot update a storage object with a name that is null or empty.");
item.ContainerName.AssertIsNotNullOrEmpty("containerName", "Cannot update a storage object with a container name that is null or empty.");
item.Name.AssertIsNotNullOrEmpty("objectName", "Cannot update a storage object with a name that is null or empty.");
var client = this.GetRestClient();
var resp = await client.UpdateObject(obj.ContainerName, obj.Name, obj.Metadata);
var resp = await client.UpdateObject(item.ContainerName, item.Name, item.Metadata);
if (resp.StatusCode != HttpStatusCode.Accepted)
{
throw new InvalidOperationException(string.Format("Failed to update storage object '{0}'. The remote server returned the following status code: '{1}'.", obj.Name, resp.StatusCode));
throw new InvalidOperationException(string.Format("Failed to update storage object '{0}'. The remote server returned the following status code: '{1}'.", item.Name, resp.StatusCode));
}
}

View File

@ -59,6 +59,46 @@ namespace Openstack.Storage
return await client.SendAsync();
}
/// <inheritdoc/>
public async Task<IHttpResponseAbstraction> CreateDynamicManifest(string containerName, string manifestName, IDictionary<string, string> metadata, string segmentsPath)
{
AssertContainerNameIsValid(containerName);
manifestName.AssertIsNotNullOrEmpty("manifestName","Cannot create a storage manifest with a null or empty name.");
segmentsPath.AssertIsNotNullOrEmpty("segmentsPath","Cannot create a dynamic large object manifest with a null or empty segments path.");
metadata.AssertIsNotNull("metadata","Cannot create a storage manifest with null metadata.");
var client = this.GetHttpClient(this.context);
client.Uri = CreateRequestUri(GetServiceEndpoint(this.context), containerName, manifestName);
client.Method = HttpMethod.Put;
client.Content = new MemoryStream();
client.Headers.Add("X-Object-Manifest", segmentsPath);
this.AddItemMetadata(metadata, client);
return await client.SendAsync();
}
/// <inheritdoc/>
public async Task<IHttpResponseAbstraction> CreateStaticManifest(string containerName, string manifestName, IDictionary<string, string> metadata, Stream content)
{
AssertContainerNameIsValid(containerName);
manifestName.AssertIsNotNullOrEmpty("manifestName", "Cannot create a storage manifest with a null or empty name.");
metadata.AssertIsNotNull("metadata", "Cannot create a storage manifest with null metadata.");
content.AssertIsNotNull("content","Cannot create a static large object manifest with null content.");
var client = this.GetHttpClient(this.context);
var baseUri = CreateRequestUri(GetServiceEndpoint(this.context), containerName);
client.Uri = new Uri(string.Format("{0}/{1}?multipart-manifest=put", baseUri, manifestName));
client.Method = HttpMethod.Put;
client.Content = content;
this.AddItemMetadata(metadata, client);
return await client.SendAsync();
}
/// <inheritdoc/>
public async Task<IHttpResponseAbstraction> CreateContainer(string containerName, IDictionary<string, string> metadata)
{
@ -196,6 +236,21 @@ namespace Openstack.Storage
return await client.SendAsync();
}
/// <inheritdoc/>
public async Task<IHttpResponseAbstraction> GetManifestMetadata(string containerName, string manifestName)
{
AssertContainerNameIsValid(containerName);
manifestName.AssertIsNotNullOrEmpty("manifestName", "Cannot get a manifest with a null or empty folder name.");
var client = this.GetHttpClient(this.context);
var baseUri = CreateRequestUri(GetServiceEndpoint(this.context), containerName);
client.Uri = new Uri(string.Format("{0}/{1}?multipart-manifest=get", baseUri, manifestName));
client.Method = HttpMethod.Get;
return await client.SendAsync();
}
/// <inheritdoc/>
public async Task<IHttpResponseAbstraction> CopyObject(string sourceContainerName, string sourceObjectName, string targetContainerName, string targetObjectName)
{