From 5202200d9440653246ec354ebc4ddb9eccae3619 Mon Sep 17 00:00:00 2001 From: "fspezi@xilium.it" Date: Wed, 8 Mar 2017 12:27:46 +0100 Subject: [PATCH 01/10] Developing ObjectStorage connector --- src/.vs/config/applicationhost.config | 1031 +++++++++++++++++ .../FSharpCodeSamples.fsproj | 30 +- src/Samples/FSharpCodeSamples/app.config | 2 +- src/corelib/Authentication/ServiceType.cs | 5 + src/corelib/Flurl/PreparedRequest.cs | 42 +- src/corelib/ObjectStorage/NamespaceDoc.cs | 13 + src/corelib/ObjectStorage/v1/Container.cs | 34 + .../ObjectStorage/v1/ContainerObject.cs | 46 + ...cessControlAllowOriginContainerMetadata.cs | 58 + .../AccessControlMaxAgeContainerMetadata.cs | 49 + .../ContainerMetadata/IContainerMetadata.cs | 10 + .../ContentLengthContainerObjectMetadata.cs | 51 + .../ContentTypeContainerObjectMetadata.cs | 56 + .../IContainerObjectMetadata.cs | 9 + .../TimestampContainerObjectMetadata.cs | 60 + .../ObjectStorage/v1/Metadata/IMetadata.cs | 26 + .../ObjectStorage/v1/Metadata/MetadataBase.cs | 44 + .../v1/ObjectStorageApiBuilder.cs | 279 +++++ .../ObjectStorage/v1/ObjectStorageService.cs | 196 ++++ .../v1/Serialization/ContainerCollection.cs | 30 + .../ContainerMetadataCollection.cs | 31 + .../ContainerObjectCollection.cs | 30 + .../ContainerObjectMetadataCollection.cs | 31 + .../Serialization/ISerializedKeyValuePair.cs | 25 + .../v1/curl-tests/accounts-get.txt | 41 + .../v1/curl-tests/containers-get.txt | 133 +++ .../v1/curl-tests/tokens-noTenantId.json | 28 + .../v1/curl-tests/tokens-withTenantId.json | 237 ++++ src/corelib/OpenStack.csproj | 164 +-- .../Serialization/CapturedStreamContent.cs | 23 + src/corelib/app.config | 2 +- src/testing/integration/App.config | 2 +- .../OpenStack.IntegrationTests.csproj | 222 ++-- src/testing/unit/OpenStack.UnitTests.csproj | 218 ++-- src/testing/unit/app.config | 2 +- 35 files changed, 2956 insertions(+), 304 deletions(-) create mode 100644 src/.vs/config/applicationhost.config create mode 100644 src/corelib/ObjectStorage/NamespaceDoc.cs create mode 100644 src/corelib/ObjectStorage/v1/Container.cs create mode 100644 src/corelib/ObjectStorage/v1/ContainerObject.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/IContainerMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/IContainerObjectMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs create mode 100644 src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs create mode 100644 src/corelib/ObjectStorage/v1/ObjectStorageService.cs create mode 100644 src/corelib/ObjectStorage/v1/Serialization/ContainerCollection.cs create mode 100644 src/corelib/ObjectStorage/v1/Serialization/ContainerMetadataCollection.cs create mode 100644 src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs create mode 100644 src/corelib/ObjectStorage/v1/Serialization/ContainerObjectMetadataCollection.cs create mode 100644 src/corelib/ObjectStorage/v1/Serialization/ISerializedKeyValuePair.cs create mode 100644 src/corelib/ObjectStorage/v1/curl-tests/accounts-get.txt create mode 100644 src/corelib/ObjectStorage/v1/curl-tests/containers-get.txt create mode 100644 src/corelib/ObjectStorage/v1/curl-tests/tokens-noTenantId.json create mode 100644 src/corelib/ObjectStorage/v1/curl-tests/tokens-withTenantId.json create mode 100644 src/corelib/Serialization/CapturedStreamContent.cs diff --git a/src/.vs/config/applicationhost.config b/src/.vs/config/applicationhost.config new file mode 100644 index 000000000..4b9bf4770 --- /dev/null +++ b/src/.vs/config/applicationhost.config @@ -0,0 +1,1031 @@ + + + + + + + +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Samples/FSharpCodeSamples/FSharpCodeSamples.fsproj b/src/Samples/FSharpCodeSamples/FSharpCodeSamples.fsproj index 54dacc5c1..e249d33de 100644 --- a/src/Samples/FSharpCodeSamples/FSharpCodeSamples.fsproj +++ b/src/Samples/FSharpCodeSamples/FSharpCodeSamples.fsproj @@ -82,25 +82,25 @@ --> - + - ..\..\..\packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll + ..\..\..\packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll True True - + - ..\..\..\packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll + ..\..\..\packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll True True - + ..\..\..\packages\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll @@ -109,7 +109,7 @@ - + ..\..\..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll @@ -118,16 +118,16 @@ - + - ..\..\..\packages\Newtonsoft.Json\lib\portable-net45+wp80+win8+wpa81+dnxcore50\Newtonsoft.Json.dll + ..\..\..\packages\Newtonsoft.Json\lib\netcore45\Newtonsoft.Json.dll True True - + ..\..\..\packages\Newtonsoft.Json\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll @@ -136,18 +136,18 @@ - - - + - - ..\..\..\packages\SimpleRESTServices\lib\net35\SimpleRESTServices.dll + + ..\..\..\packages\Newtonsoft.Json\lib\portable-net45+wp80+win8+wpa81+aspnetcore50\Newtonsoft.Json.dll True True - + + + ..\..\..\packages\SimpleRESTServices\lib\net40\SimpleRESTServices.dll diff --git a/src/Samples/FSharpCodeSamples/app.config b/src/Samples/FSharpCodeSamples/app.config index 0ed82eae5..a38c807f2 100644 --- a/src/Samples/FSharpCodeSamples/app.config +++ b/src/Samples/FSharpCodeSamples/app.config @@ -5,6 +5,6 @@ True - + \ No newline at end of file diff --git a/src/corelib/Authentication/ServiceType.cs b/src/corelib/Authentication/ServiceType.cs index 6862a822a..89e11bec5 100644 --- a/src/corelib/Authentication/ServiceType.cs +++ b/src/corelib/Authentication/ServiceType.cs @@ -84,6 +84,11 @@ public override int GetHashCode() /// The Networking service /// public static readonly ServiceType Networking = new ServiceType("network"); + + /// + /// The ObjectStorage service + /// + public static readonly ServiceType ObjectStorage = new ServiceType("object-storage"); } /// diff --git a/src/corelib/Flurl/PreparedRequest.cs b/src/corelib/Flurl/PreparedRequest.cs index 1d96b204a..c434ac959 100644 --- a/src/corelib/Flurl/PreparedRequest.cs +++ b/src/corelib/Flurl/PreparedRequest.cs @@ -1,9 +1,13 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Flurl.Http.Content; +using OpenStack.ObjectStorage.v1.Serialization; +using OpenStack.Serialization; // ReSharper disable once CheckNamespace namespace Flurl.Http @@ -79,6 +83,15 @@ public PreparedRequest(Url url, bool autoDispose) : base(url, autoDispose) CancellationToken = cancellationToken; return this; } + /// + /// Prepares the client to send a DELETE request + /// + public PreparedRequest PrepareHead(CancellationToken cancellationToken = default(CancellationToken)) + { + Verb = HttpMethod.Head; + CancellationToken = cancellationToken; + return this; + } /// /// Prepares the client to send a GET request @@ -100,7 +113,7 @@ public PreparedRequest(Url url, bool autoDispose) : base(url, autoDispose) CancellationToken = cancellationToken; return this; } - + /// /// Prepares the client to send a POST request containing json /// @@ -112,6 +125,22 @@ public PreparedRequest(Url url, bool autoDispose) : base(url, autoDispose) return this; } + /// + /// Prepares the client to send a POST request containing data in header + /// + public PreparedRequest PreparePostHeader(IEnumerable data, CancellationToken cancellationToken = default(CancellationToken)) + { + Verb = HttpMethod.Post; + foreach (var keyValuePair in data) + { + this.WithHeader(keyValuePair.Key, keyValuePair.Value); + } + CancellationToken = cancellationToken; + return this; + } + + + /// /// Prepares the client to send a PUT request containing json /// @@ -123,6 +152,17 @@ public PreparedRequest(Url url, bool autoDispose) : base(url, autoDispose) return this; } + /// + /// Prepares the client to send a PUT request containing data in stream + /// + public PreparedRequest PreparePutStream(System.IO.Stream dataStream, CancellationToken cancellationToken = default(CancellationToken)) + { + Verb = HttpMethod.Put; + Content = new CapturedStreamContent(dataStream); + CancellationToken = cancellationToken; + return this; + } + /// /// Executes the built request /// diff --git a/src/corelib/ObjectStorage/NamespaceDoc.cs b/src/corelib/ObjectStorage/NamespaceDoc.cs new file mode 100644 index 000000000..d6263aada --- /dev/null +++ b/src/corelib/ObjectStorage/NamespaceDoc.cs @@ -0,0 +1,13 @@ +namespace OpenStack.ObjectStorage +{ + using System.Runtime.CompilerServices; + + /// + /// The namespace defines provider-independent + /// interfaces and implementations for the OpenStack ObjectStorage API. + /// + [CompilerGenerated] + internal class NamespaceDoc + { + } +} diff --git a/src/corelib/ObjectStorage/v1/Container.cs b/src/corelib/ObjectStorage/v1/Container.cs new file mode 100644 index 000000000..3b66ddaa2 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Container.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace OpenStack.ObjectStorage.v1 { + + /// + /// Represents a Container + /// + public class Container { + + /// + /// Container name + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Count of objects in container + /// + [JsonProperty("count")] + public long Count { get; set; } + + /// + /// Weight of container + /// + [JsonProperty("bytes")] + public long Bytes { get; set; } + + } +} diff --git a/src/corelib/ObjectStorage/v1/ContainerObject.cs b/src/corelib/ObjectStorage/v1/ContainerObject.cs new file mode 100644 index 000000000..c85776514 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContainerObject.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace OpenStack.ObjectStorage.v1 { + + /// + /// Represent an Object of container + /// + public class ContainerObject { + + /// + /// Hash of Object + /// + [JsonProperty("hash")] + public string Hash { get; set; } + + /// + /// Date of last update + /// + [JsonProperty("last_modified")] + public DateTime LastModified { get; set; } + + /// + /// Weight of object + /// + [JsonProperty("bytes")] + public long Bytes { get; set; } + + /// + /// Object name + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Object content type + /// + [JsonProperty("content_type")] + public string ContentType { get; set; } + + } +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs new file mode 100644 index 000000000..33fc8bc11 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Serialization; + +namespace OpenStack.ObjectStorage.v1.Metadata.ContainerMetadata { + + /// + /// The `Access-Control-Allow-Origin` header metadata. + /// + public class AccessControlAllowOriginContainerMetadata : MetadataBase, IContainerMetadata + { + + /// + /// Create new instance + /// + public AccessControlAllowOriginContainerMetadata() : base("Access-Control-Allow-Origin") + { + + } + + /// + /// Get or set list of allowed origins. Example: [ "http://web.com", "https://web.com" ] + /// + public string[] Origins + { + get { return parseValue(this.Value); } + set { this.Value = serializeValue(value); } + } + + /// + /// Serialize value to Metadata + /// + /// + private static string serializeValue(string[] value) + { + if (value == null) return ""; + + return string.Join(" ", value); + } + + /// + /// Parse value from Metadata + /// + /// + private static string[] parseValue(string value) + { + if (string.IsNullOrEmpty(value)) + { + return null; + } + + return value.Split(new [] {' '}, StringSplitOptions.RemoveEmptyEntries); + } + } +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs new file mode 100644 index 000000000..2bfbda00b --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.Metadata.ContainerMetadata { + + /// + /// The `Access-Control-Max-Age` header metadata. + /// + public class AccessControlMaxAgeContainerMetadata : MetadataBase, IContainerMetadata { + + /// + /// Create new instance + /// + public AccessControlMaxAgeContainerMetadata() : base("Access-Control-Max-Age") + { + + } + + /// + /// Get or set the max age of container objects, in seconds. + /// + public long MaxAgeSeconds + { + get { return parseValue(this.Value); } + set { this.Value = serializeValue(value); } + } + + /// + /// Serialize value to Metadata + /// + /// + private static string serializeValue(long value) + { + return value.ToString("0"); + } + + /// + /// Parse value from Metadata + /// + /// + private static long parseValue(string value) + { + return System.Convert.ToInt64(value); + } + } +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/IContainerMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/IContainerMetadata.cs new file mode 100644 index 000000000..6455a8584 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/IContainerMetadata.cs @@ -0,0 +1,10 @@ +namespace OpenStack.ObjectStorage.v1.Metadata.ContainerMetadata { + + /// + /// Represents a Container metadata + /// + public interface IContainerMetadata : IMetadata { + + } + +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs new file mode 100644 index 000000000..e28ef71e0 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Serialization; + +namespace OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata { + + /// + /// The `Content-Length` header metadata. + /// + public class ContentLengthContainerObjectMetadata : MetadataBase, IContainerObjectMetadata + { + + /// + /// Create new instance + /// + public ContentLengthContainerObjectMetadata() : base("Content-Length") + { + + } + + /// + /// Get or set content-length of Object + /// + public long ContentLength + { + get { return parseValue(this.Value); } + set { this.Value = serializeValue(value); } + } + + /// + /// Serialize value to Metadata + /// + /// + private static string serializeValue(long value) + { + return value.ToString("0"); + } + + /// + /// Parse value from Metadata + /// + /// + private static long parseValue(string value) + { + return System.Convert.ToInt64(value); + } + } +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs new file mode 100644 index 000000000..39125bc63 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Serialization; + +namespace OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata { + + /// + /// The `Content-Type` header metadata. + /// + public class ContentTypeContainerObjectMetadata : MetadataBase, IContainerObjectMetadata + { + + /// + /// Create new instance + /// + public ContentTypeContainerObjectMetadata() : base("Content-Type") + { + + } + + /// + /// Get or set content-type of Object + /// + public string ContentType + { + get { return parseValue(this.Value); } + set { this.Value = serializeValue(value); } + } + + /// + /// Serialize value to Metadata + /// + /// + private static string serializeValue(string value) + { + return value ?? ""; + } + + /// + /// Parse value from Metadata + /// + /// + private static string parseValue(string value) + { + if (string.IsNullOrEmpty(value)) + { + return null; + } + + return value; + } + } +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/IContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/IContainerObjectMetadata.cs new file mode 100644 index 000000000..4c368c36e --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/IContainerObjectMetadata.cs @@ -0,0 +1,9 @@ +namespace OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata { + + /// + /// Represents a Object metadata + /// + public interface IContainerObjectMetadata : IMetadata { + + } +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs new file mode 100644 index 000000000..61645a266 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Serialization; + +namespace OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata { + + /// + /// The `X-Timestamp` header metadata. + /// + public class TimestampContainerObjectMetadata : MetadataBase, IContainerObjectMetadata + { + + private static readonly DateTime zeroDayUnixTime = new DateTime(1970, 1, 1, 0, 0, 0, 0); + + /// + /// Create new instance + /// + public TimestampContainerObjectMetadata() : base("X-Timestamp") + { + + } + + /// + /// Get or set LastUpdate of Object + /// + public DateTime LastUpdate + { + get { return parseValue(this.Value); } + set { this.Value = serializeValue(value); } + } + + /// + /// Serialize value to Metadata + /// + /// + private static string serializeValue(DateTime value) + { + return value.Subtract(zeroDayUnixTime).TotalDays.ToString("R", System.Globalization.CultureInfo.InvariantCulture); + } + + /// + /// Parse value from Metadata + /// + /// + private static DateTime parseValue(string value) + { + if (string.IsNullOrEmpty(value)) + { + return zeroDayUnixTime; + } + + var valueDays = System.Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture); + + return zeroDayUnixTime.AddDays(valueDays); + } + } +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs new file mode 100644 index 000000000..e5039277d --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenStack.ObjectStorage.v1.Serialization; + +namespace OpenStack.ObjectStorage.v1.Metadata { + + /// + /// Generic metadata + /// + public interface IMetadata : ISerializedKeyValuePair { + + /// + /// Header key of Metatag + /// + string MetadataKey { get; } + + /// + /// Value of Metatag + /// + new string Value { get; set; } + + } +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs b/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs new file mode 100644 index 000000000..994f04003 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using OpenStack.ObjectStorage.v1.Serialization; + +namespace OpenStack.ObjectStorage.v1.Metadata { + + /// + /// Base implementation of generic Metadata + /// + public abstract class MetadataBase : IMetadata, ISerializedKeyValuePair { + + /// + /// Create new . + /// + /// + public MetadataBase(string metadataKey) + { + this.MetadataKey = metadataKey; + + } + + /// + /// Get metadata Key + /// + [JsonProperty] + public string MetadataKey { get; } + + /// + /// Get or set value + /// + [JsonProperty] + public string Value { get; set; } + + string ISerializedKeyValuePair.Key + { + get { return this.MetadataKey; } + set { throw new InvalidOperationException(); } + } + } +} diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs b/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs new file mode 100644 index 000000000..fcc248752 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs @@ -0,0 +1,279 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Flurl; +using Flurl.Extensions; +using Flurl.Http; +using OpenStack.Authentication; +using OpenStack.Networking.v2.Serialization; +using OpenStack.ObjectStorage.v1.Serialization; +using OpenStack.Serialization; + +namespace OpenStack.ObjectStorage.v1 +{ + /// + /// Builds requests to the Networking API which can be further customized and then executed. + /// Intended for custom implementations. + /// + /// OpenStack Networking API v2 Reference + public class ObjectStorageApiBuilder + { + /// + protected readonly IAuthenticationProvider AuthenticationProvider; + + /// + protected readonly ServiceEndpoint Endpoint; + + /// + /// Initializes a new instance of the class. + /// + /// The service type for the desired networking provider. + /// The authentication provider. + /// The region. + /// if set to true uses the internal URLs specified in the ServiceCatalog, otherwise the public URLs are used. + public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider authenticationProvider, string region, bool useInternalUrl) + { + if(serviceType == null) + throw new ArgumentNullException("serviceType"); + if (authenticationProvider == null) + throw new ArgumentNullException("authenticationProvider"); + if (string.IsNullOrEmpty(region)) + throw new ArgumentException("region cannot be null or empty", "region"); + + AuthenticationProvider = authenticationProvider; + Endpoint = new ServiceEndpoint(serviceType, authenticationProvider, region, useInternalUrl); + } + + #region Tenants + /* + /// + /// Returns details of current Tenant. + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Details of current Tenant. + /// + public async Task GetCurrentTenantAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .Authenticate(AuthenticationProvider) + .PrepareGet(cancellationToken); + } + */ + #endregion + + #region Containers + /// + /// Lists all containers associated with the current tenant. + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// A collection of containers associated with current tenant. + /// + public async Task ListContainersAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .Authenticate(AuthenticationProvider) + .PrepareGet(cancellationToken); + } + + /// + /// Gets the container content of specified container. + /// + /// The container identifier. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task GetContainerContentAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegments(containerId) + .Authenticate(AuthenticationProvider) + .PrepareGet(cancellationToken); + } + + /// + /// Gets the container content of specified container. + /// + /// The container identifier. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task CreateContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + return await new Task(() => + { + throw new NotImplementedException(); + }); + } + + /// + /// Update metadata to container. + /// + /// The container identifier. + /// The metadata collection. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task SaveContainerMetadataAsync(string containerId, ContainerMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegments(containerId) + .Authenticate(AuthenticationProvider) + .PreparePostHeader(metadataCollection, cancellationToken); + } + + /// + /// Gets the metadata from container. + /// + /// The container identifier. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task ReadContainerMetadataAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegments(containerId) + .Authenticate(AuthenticationProvider) + .PrepareHead(cancellationToken); + } + + /// + /// Deletes the specified container. + /// + /// The container identifier. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + public virtual async Task DeleteContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return (PreparedRequest)endpoint + .AppendPathSegments(containerId) + .Authenticate(AuthenticationProvider) + .PrepareDelete(cancellationToken) + .AllowHttpStatus(HttpStatusCode.NotFound); + } + + #endregion + + + #region Container Objects + + /// + /// Gets the object in specified container. + /// + /// The container identifier. + /// The path of object. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task GetContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegments(containerId, objectPath) + .Authenticate(AuthenticationProvider) + .PrepareGet(cancellationToken); + } + + /// + /// Add or update the object to specified container. + /// + /// The container identifier. + /// The path of object. + /// Stream to obtains content of Object + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Nothing + /// + public virtual async Task UpdateContainerObjectAsync(string containerId, string objectPath, System.IO.Stream dataStream, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegments(containerId, objectPath) + .Authenticate(AuthenticationProvider) + .PreparePutStream(dataStream, cancellationToken); + } + + /// + /// Delete the object in specified container. + /// + /// The container identifier. + /// The path of object. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Nothing + /// + public virtual async Task DeleteContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegments(containerId, objectPath) + .Authenticate(AuthenticationProvider) + .PrepareDelete(cancellationToken); + } + + /// + /// Get the metadata of object in specified container. + /// + /// The container identifier. + /// The path of object. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Metadata of container object + /// + public virtual async Task ReadContainerObjectMetadataAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegments(containerId, objectPath) + .Authenticate(AuthenticationProvider) + .PrepareHead(cancellationToken); + } + + /// + /// Save the metadata of object in specified container. + /// + /// The container identifier. + /// The path of object. + /// The metadata to save in object. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Nothing + /// + public virtual async Task SaveContainerObjectMetadataAsync(string containerId, string objectPath, ContainerObjectMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegments(containerId, objectPath) + .Authenticate(AuthenticationProvider) + .PreparePostHeader(metadataCollection, cancellationToken); + } + + + #endregion + + } +} diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs new file mode 100644 index 000000000..cf7e04677 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs @@ -0,0 +1,196 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Flurl.Extensions; +using Flurl.Http; +using OpenStack.Authentication; +using OpenStack.Networking.v2.Serialization; +using OpenStack.ObjectStorage.v1.Serialization; + +namespace OpenStack.ObjectStorage.v1 +{ + /// + /// The OpenStack Networking Service. + /// + /// OpenStack Networking API v2 Overview + /// OpenStack Networking API v2 Reference + public class ObjectStorageService + { + internal readonly ObjectStorageApiBuilder _objectStorageApiBuilder; + + /// + /// Initializes a new instance of the class. + /// + /// The authentication provider. + /// The region. + /// if set to true uses the internal URLs specified in the ServiceCatalog, otherwise the public URLs are used. + public ObjectStorageService(IAuthenticationProvider authenticationProvider, string region, bool useInternalUrl = false) + { + _objectStorageApiBuilder = new ObjectStorageApiBuilder(ServiceType.ObjectStorage, authenticationProvider, region, useInternalUrl); + } + + #region Containers + /// + public async Task> ListContainersAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .ListContainersAsync(cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public Task GetContainerContentAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .GetContainerContentAsync(containerId, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public Task CreateContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .CreateContainerAsync(containerId, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public async Task SaveContainerMetadataAsync(string containerId, ContainerMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .SaveContainerMetadataAsync(containerId, metadataCollection, cancellationToken) + .SendAsync() + .ReceiveString(); + } + + /// + public Task ReadContainerMetadataAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .ReadContainerMetadataAsync(containerId, cancellationToken) + .SendAsync() + .ReceiveString(); + } + + /// + public Task DeleteContainerAsync(Identifier networkId, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .DeleteContainerAsync(networkId, cancellationToken) + .SendAsync(); + } + #endregion + + #region Subnets + + /// + public async Task> ListSubnetsAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .ListSubnetsAsync(cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public Task CreateSubnetAsync(SubnetCreateDefinition subnet, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .CreateSubnetAsync(subnet, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public async Task> CreateSubnetsAsync(IEnumerable subnets, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .CreateSubnetsAsync(subnets, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public Task GetSubnetAsync(Identifier subnetId, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .GetSubnetAsync(subnetId, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public Task UpdateSubnetAsync(Identifier subnetId, SubnetUpdateDefinition subnet, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .UpdateSubnetAsync(subnetId, subnet, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public Task DeleteSubnetAsync(Identifier subnetId, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .DeleteSubnetAsync(subnetId, cancellationToken) + .SendAsync(); + } + #endregion + + #region Ports + + /// + public async Task> ListPortsAsync(PortListOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder.ListPortsAsync(options, cancellationToken); + } + + /// + public Task CreatePortAsync(PortCreateDefinition port, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .CreatePortAsync(port, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public async Task> CreatePortsAsync(IEnumerable ports, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .CreatePortsAsync(ports, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public Task GetPortAsync(Identifier portId, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .GetPortAsync(portId, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public Task UpdatePortAsync(Identifier portId, PortUpdateDefinition port, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .UpdatePortAsync(portId, port, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public Task DeletePortAsync(Identifier portId, CancellationToken cancellationToken = default(CancellationToken)) + { + return _objectStorageApiBuilder + .DeletePortAsync(portId, cancellationToken) + .SendAsync(); + } + #endregion + } +} diff --git a/src/corelib/ObjectStorage/v1/Serialization/ContainerCollection.cs b/src/corelib/ObjectStorage/v1/Serialization/ContainerCollection.cs new file mode 100644 index 000000000..9c9bb4a79 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Serialization/ContainerCollection.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using OpenStack.Serialization; + +namespace OpenStack.ObjectStorage.v1.Serialization +{ + /// + /// Represents a collection of Object resources returned by the . + /// Intended for custom implementations and stubbing responses in unit tests. + /// + /// + /// + [JsonConverterWithConstructor(typeof(RootWrapperConverter), "containers")] + public class ContainerCollection : List + { + /// + /// Initializes a new instance of the class. + /// + public ContainerCollection() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The containers. + public ContainerCollection(IEnumerable containers) : base(containers) + { + } + } +} \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/Serialization/ContainerMetadataCollection.cs b/src/corelib/ObjectStorage/v1/Serialization/ContainerMetadataCollection.cs new file mode 100644 index 000000000..ba5f80302 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Serialization/ContainerMetadataCollection.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using OpenStack.ObjectStorage.v1.Metadata; +using OpenStack.ObjectStorage.v1.Metadata.ContainerMetadata; +using OpenStack.Serialization; + +namespace OpenStack.ObjectStorage.v1.Serialization +{ + /// + /// Represents a collection of Container Metadata. + /// + /// + /// + [JsonConverterWithConstructor(typeof(RootWrapperConverter), "containerMetadataList")] + public class ContainerMetadataCollection : List + { + /// + /// Initializes a new instance of the class. + /// + public ContainerMetadataCollection() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The list of metadata. + public ContainerMetadataCollection(IEnumerable containerMetadataList) : base(containerMetadataList) + { + } + } +} \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs b/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs new file mode 100644 index 000000000..03014b208 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using OpenStack.Serialization; + +namespace OpenStack.ObjectStorage.v1.Serialization +{ + /// + /// Represents a collection of Object resources returned by the . + /// Intended for custom implementations and stubbing responses in unit tests. + /// + /// + /// + [JsonConverterWithConstructor(typeof(RootWrapperConverter), "containerObjects")] + public class ContainerObjectCollection : List + { + /// + /// Initializes a new instance of the class. + /// + public ContainerObjectCollection() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The container objects. + public ContainerObjectCollection(IEnumerable containerObjects) : base(containerObjects) + { + } + } +} \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectMetadataCollection.cs b/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectMetadataCollection.cs new file mode 100644 index 000000000..d71f73eb6 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectMetadataCollection.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using OpenStack.ObjectStorage.v1.Metadata; +using OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata; +using OpenStack.Serialization; + +namespace OpenStack.ObjectStorage.v1.Serialization +{ + /// + /// Represents a collection of Container Object Metadata. + /// + /// + /// + [JsonConverterWithConstructor(typeof(RootWrapperConverter), "metadataList")] + public class ContainerObjectMetadataCollection : List + { + /// + /// Initializes a new instance of the class. + /// + public ContainerObjectMetadataCollection() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The list of metadata. + public ContainerObjectMetadataCollection(IEnumerable metadataList) : base(metadataList) + { + } + } +} \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/Serialization/ISerializedKeyValuePair.cs b/src/corelib/ObjectStorage/v1/Serialization/ISerializedKeyValuePair.cs new file mode 100644 index 000000000..cc5118c44 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Serialization/ISerializedKeyValuePair.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.Serialization { + + /// + /// Represents a generic serialized collection's item + /// + public interface ISerializedKeyValuePair { + + /// + /// Key of item + /// + string Key { get; set; } + + /// + /// Value of item + /// + string Value { get; set; } + + } +} diff --git a/src/corelib/ObjectStorage/v1/curl-tests/accounts-get.txt b/src/corelib/ObjectStorage/v1/curl-tests/accounts-get.txt new file mode 100644 index 000000000..27afa23f7 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/curl-tests/accounts-get.txt @@ -0,0 +1,41 @@ +# Guida +https://developer.openstack.org/api-ref/object-storage/?expanded=show-account-details-and-list-containers-detail + +# Chiamata: +curl -i https://storage.sbg1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d?format=json -X GET -H "X-Auth-Token: f163a4eb716d45178e6f72a8103cb711" + +# Output: +HTTP/1.1 200 OK +Content-Length: 162 +X-Account-Container-Count: 3 +X-Account-Object-Count: 28 +X-Account-Storage-Policy-Pcs-Object-Count: 28 +X-Account-Storage-Policy-Pcs-Bytes-Used: 5415613 +X-Account-Meta-Temp-Url-Key: c3S5GHmD4JKv4dYz +X-Account-Storage-Policy-Pcs-Container-Count: 3 +X-Account-Bytes-Used: 5415613 +X-Timestamp: 1435826097.48927 +Content-Type: application/json; charset=utf-8 +Accept-Ranges: bytes +x-account-project-domain-id: default +X-Trans-Id: tx6f14498ede1949ad935b2-0058bf0354 +X-Openstack-Request-Id: tx6f14498ede1949ad935b2-0058bf0354 +Date: Tue, 07 Mar 2017 19:00:36 GMT + +[ + { + "count": 8, + "bytes": 232691, + "name": "Prova hosting" + }, + { + "count": 5, + "bytes": 12517, + "name": "Prova public" + }, + { + "count": 15, + "bytes": 5170405, + "name": "fs-test" + } +] \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/curl-tests/containers-get.txt b/src/corelib/ObjectStorage/v1/curl-tests/containers-get.txt new file mode 100644 index 000000000..86038253c --- /dev/null +++ b/src/corelib/ObjectStorage/v1/curl-tests/containers-get.txt @@ -0,0 +1,133 @@ +# Guida +https://developer.openstack.org/api-ref/object-storage/?expanded=show-container-details-and-list-objects-detail + +# Chiamata: +curl -i https://storage.sbg1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d/fs-test?format=json -X GET -H "X-Auth-Token: f163a4eb716d45178e6f72a8103cb711" + +# Output: +HTTP/1.1 200 OK +X-Container-Meta-Web-Listings: true +Content-Length: 2490 +X-Container-Object-Count: 15 +X-Timestamp: 1488310678.00786 +X-Container-Meta-Web-Error: error.html +X-Container-Meta-Web-Listings-Css: listing.css +X-Storage-Policy: PCS +Last-Modified: Tue, 07 Mar 2017 13:56:22 GMT +X-Container-Meta-Web-Index: index.html +X-Container-Meta-Access-Control-Allow-Origin: https://www.ovh.com +X-Container-Read: .r:*,.rlistings +X-Container-Bytes-Used: 5170405 +Content-Type: application/json; charset=utf-8 +Accept-Ranges: bytes +X-Trans-Id: tx6ce0070ba54f4cd9b399d-0058bf0500 +X-Openstack-Request-Id: tx6ce0070ba54f4cd9b399d-0058bf0500 +Date: Tue, 07 Mar 2017 19:07:44 GMT + +[ + { + "hash":"84bb0c02a4a70cc3963cb17ae40ed47d", + "last_modified":"2017-02-28T19:37:58.628560", + "bytes":447, + "name":"401error.html", + "content_type":"text/html" + }, + { + "hash":"144f46132883febbd8525714c44fa1ce", + "last_modified":"2017-02-28T19:37:58.573220", + "bytes":438, + "name":"404error.html", + "content_type":"text/html" + }, + { + "hash":"1ad5d67e460e381aa5c87e5875302493", + "last_modified":"2017-02-28T19:38:00.384590", + "bytes":468, + "name":"503error.html", + "content_type":"text/html" + }, + { + "hash":"e30ce861ccc126a42a644808aba0bb43", + "last_modified":"2017-03-07T08:20:53.227720", + "bytes":2830555, + "name":"W3SVC4-2017-02.zip", + "content_type":"application/zip" + }, + { + "hash":"e52aa38131bb45c1e539b793f44acf72", + "last_modified":"2017-02-28T19:37:58.641620", + "bytes":87308, + "name":"images/401.png", + "content_type":"image/png" + }, + { + "hash":"f2fdf1aa043b12ee4b1d30d54a502619", + "last_modified":"2017-02-28T19:37:58.625040", + "bytes":43747, + "name":"images/404.png", + "content_type":"image/png" + }, + { + "hash":"bbb1d6da3f2137d19b879d9e26be95ef", + "last_modified":"2017-02-28T19:37:58.639810", + "bytes":90846, + "name":"images/503.png", + "content_type":"image/png" + }, + { + "hash":"0bbe5b868c589446365c190435918830", + "last_modified":"2017-02-28T19:37:58.635780", + "bytes":7695, + "name":"images/index.png", + "content_type":"image/png" + }, + { + "hash":"8c730d91d7a74c5aba0cd1093c63e04b", + "last_modified":"2017-02-28T19:37:58.638280", + "bytes":313, + "name":"index.html", + "content_type":"text/html" + }, + { + "hash":"e7cc89e8daeb2be27d008b1ec5ef9480", + "last_modified":"2017-02-28T19:37:58.633380", + "bytes":1876, + "name":"listing.css", + "content_type":"text/css" + }, + { + "hash":"d960cecd63061c475603c586a722aa1e", + "last_modified":"2017-02-28T19:40:15.638860", + "bytes":832995, + "name":"path1/Negozio 1.jpg", + "content_type":"image/jpeg" + }, + { + "hash":"caa10a4f0423ed70fd7ea49af2750af1", + "last_modified":"2017-02-28T19:40:12.019660", + "bytes":745340, + "name":"path1/musica-dettaglio.jpg", + "content_type":"image/jpeg" + }, + { + "hash":"3bc9754b380c0ba4c260f27d9165cbaf", + "last_modified":"2017-02-28T19:40:11.977410", + "bytes":114224, + "name":"path1/natura-tramonto.jpg", + "content_type":"image/jpeg" + }, + { + "hash":"8c1c0175fe4349810ad19c49f65eb7bc", + "last_modified":"2017-02-28T19:40:11.975840", + "bytes":414035, + "name":"path1/natura.jpg", + "content_type":"image/jpeg" + }, + { + "hash":"9467c8b0c0103dc18b9ba274c9c87abf", + "last_modified":"2017-02-28T19:37:58.629130", + "bytes":118, + "name":"styles/index.css", + "content_type":"text/css" + } +] \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/curl-tests/tokens-noTenantId.json b/src/corelib/ObjectStorage/v1/curl-tests/tokens-noTenantId.json new file mode 100644 index 000000000..253ab0ecc --- /dev/null +++ b/src/corelib/ObjectStorage/v1/curl-tests/tokens-noTenantId.json @@ -0,0 +1,28 @@ +// Chiamata: +// curl -s -X POST https://auth.cloud.ovh.net/v2.0/tokens -H "Content-Type: application/json" -d '{"auth": {"tenantId":"", "passwordCredentials": {"username": "ZSymZdG4dJkw", "password": "VBXpZae2K3Ak4NDH6b2ZW4xYjYfD9EZQ"}}}' | python -m json.tool + +// Output: +{ + "access": { + "metadata": { + "is_admin": 0, + "roles": [] + }, + "serviceCatalog": [], + "token": { + "audit_ids": [ + "_siZ0-sSToWj3dueo18hbQ" + ], + "expires": "2017-03-08T18:30:43Z", + "id": "8c10f0f1259a4d899d700853afeca481", + "issued_at": "2017-03-07T18:30:43.240456" + }, + "user": { + "id": "931b49af0fd64cfe8e643fa732083290", + "name": "ZSymZdG4dJkw", + "roles": [], + "roles_links": [], + "username": "ZSymZdG4dJkw" + } + } +} \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/curl-tests/tokens-withTenantId.json b/src/corelib/ObjectStorage/v1/curl-tests/tokens-withTenantId.json new file mode 100644 index 000000000..27e866c75 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/curl-tests/tokens-withTenantId.json @@ -0,0 +1,237 @@ +// Chiamata: +// curl -s -X POST https://auth.cloud.ovh.net/v2.0/tokens -H "Content-Type: application/json" -d '{"auth": {"tenantId":"9aa1ef7de0284ddd9d9cf0f620a99b9d", "passwordCredentials": {"username": "ZSymZdG4dJkw", "password": "VBXpZae2K3Ak4NDH6b2ZW4xYjYfD9EZQ"}}}' | python -m json.tool + +// Output: +{ + "access": { + "metadata": { + "is_admin": 0, + "roles": [ + "9fe2ff9ee4384b1894a90878d3e92bab" + ] + }, + "serviceCatalog": [ + { + "endpoints": [ + { + "adminURL": "https://compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "id": "17f6ef1cc63e492ab8d3f2bda8428cb0", + "internalURL": "https://compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "GRA1" + }, + { + "adminURL": "https://compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "id": "21fdd202afd04470bbaf84f9396d0dcc", + "internalURL": "https://compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "BHS1" + }, + { + "adminURL": "https://compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "id": "a707bffedf1c4b80a124c585c67c1639", + "internalURL": "https://compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "SBG1" + } + ], + "endpoints_links": [], + "name": "nova", + "type": "compute" + }, + { + "endpoints": [ + { + "adminURL": "https://network.compute.gra1.cloud.ovh.net/", + "id": "26a339a8c7d5463f89ca937068ebbcd4", + "internalURL": "https://network.compute.gra1.cloud.ovh.net/", + "publicURL": "https://network.compute.gra1.cloud.ovh.net/", + "region": "GRA1" + }, + { + "adminURL": "https://network.compute.bhs1.cloud.ovh.net/", + "id": "3fe2326789ec4e37af2e6b2c80a90876", + "internalURL": "https://network.compute.bhs1.cloud.ovh.net/", + "publicURL": "https://network.compute.bhs1.cloud.ovh.net/", + "region": "BHS1" + }, + { + "adminURL": "https://network.compute.sbg1.cloud.ovh.net/", + "id": "075839111e7a41f1bb458926e5f04cec", + "internalURL": "https://network.compute.sbg1.cloud.ovh.net/", + "publicURL": "https://network.compute.sbg1.cloud.ovh.net/", + "region": "SBG1" + } + ], + "endpoints_links": [], + "name": "neutron", + "type": "network" + }, + { + "endpoints": [ + { + "adminURL": "https://volume.compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "id": "7231957fdf0346e5adebe860ac5e5e57", + "internalURL": "https://volume.compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://volume.compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "GRA1" + }, + { + "adminURL": "https://volume.compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "id": "2f5b68f95d7b4b1fad1a683dac8e8ca3", + "internalURL": "https://volume.compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://volume.compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "BHS1" + }, + { + "adminURL": "https://volume.compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "id": "021b61bd7313479e8f8d77d21c7b434a", + "internalURL": "https://volume.compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://volume.compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "SBG1" + } + ], + "endpoints_links": [], + "name": "cinderv2", + "type": "volumev2" + }, + { + "endpoints": [ + { + "adminURL": "https://image.compute.gra1.cloud.ovh.net/", + "id": "56795c82f1744e47b7782f1fc2407212", + "internalURL": "https://image.compute.gra1.cloud.ovh.net/", + "publicURL": "https://image.compute.gra1.cloud.ovh.net/", + "region": "GRA1" + }, + { + "adminURL": "https://image.compute.bhs1.cloud.ovh.net/", + "id": "5eaa4cbe80354ea482f2b0477c9c16f0", + "internalURL": "https://image.compute.bhs1.cloud.ovh.net/", + "publicURL": "https://image.compute.bhs1.cloud.ovh.net/", + "region": "BHS1" + }, + { + "adminURL": "https://image.compute.sbg1.cloud.ovh.net/", + "id": "15758b246d1340e887a2170bd3399071", + "internalURL": "https://image.compute.sbg1.cloud.ovh.net/", + "publicURL": "https://image.compute.sbg1.cloud.ovh.net/", + "region": "SBG1" + } + ], + "endpoints_links": [], + "name": "glance", + "type": "image" + }, + { + "endpoints": [ + { + "adminURL": "https://volume.compute.gra1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "id": "a6936c8876c1490cbf91d0707e78d350", + "internalURL": "https://volume.compute.gra1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://volume.compute.gra1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "GRA1" + }, + { + "adminURL": "https://volume.compute.bhs1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "id": "43bc107cf78448faa9e5a6b3a5ca48dd", + "internalURL": "https://volume.compute.bhs1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://volume.compute.bhs1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "BHS1" + }, + { + "adminURL": "https://volume.compute.sbg1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "id": "2be04ee1ddb148c19e91d3da5934fa55", + "internalURL": "https://volume.compute.sbg1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://volume.compute.sbg1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "SBG1" + } + ], + "endpoints_links": [], + "name": "cinder", + "type": "volume" + }, + { + "endpoints": [ + { + "adminURL": "https://storage.gra1.cloud.ovh.net", + "id": "c96f61d071a74e36bd3c07e53d241ce3", + "internalURL": "http://127.0.0.1:8888/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://storage.gra1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "GRA1" + }, + { + "adminURL": "https://storage.bhs1.cloud.ovh.net:8888/", + "id": "3327534a1a824389aae5d663b9821d67", + "internalURL": "http://127.0.0.1:8888/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://storage.bhs1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "BHS1" + }, + { + "adminURL": "https://storage.sbg1.cloud.ovh.net", + "id": "2af96b87ad484cb7879a9ea554d5418c", + "internalURL": "http://127.0.0.1:8888/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", + "publicURL": "https://storage.sbg1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", + "region": "SBG1" + } + ], + "endpoints_links": [], + "name": "swift", + "type": "object-store" + }, + { + "endpoints": [ + { + "adminURL": "https://auth.cloud.ovh.net:35357/v2.0", + "id": "62101e498fc3404dbc18ec80888992cb", + "internalURL": "http://127.0.0.1:5000/v2.0", + "publicURL": "https://auth.cloud.ovh.net/v2.0", + "region": "GRA1" + }, + { + "adminURL": "https://auth.cloud.ovh.net:35357/v2.0", + "id": "00e403276b3246c4a5c54dc7133f9f0a", + "internalURL": "http://127.0.0.1:5000/v2.0", + "publicURL": "https://auth.cloud.ovh.net/v2.0", + "region": "BHS1" + }, + { + "adminURL": "https://auth.cloud.ovh.net:35357/v2.0", + "id": "6094ef2ed9f240ed9b648dfcc0d9f923", + "internalURL": "http://127.0.0.1:5000/v2.0", + "publicURL": "https://auth.cloud.ovh.net/v2.0", + "region": "SBG1" + } + ], + "endpoints_links": [], + "name": "keystone", + "type": "identity" + } + ], + "token": { + "audit_ids": [ + "LevRPD9ZQtWhK5Ejw4_gBA" + ], + "expires": "2017-03-08T18:33:38Z", + "id": "f163a4eb716d45178e6f72a8103cb711", + "issued_at": "2017-03-07T18:33:38.344366", + "tenant": { + "description": null, + "enabled": true, + "id": "9aa1ef7de0284ddd9d9cf0f620a99b9d", + "name": "9433942458871390" + } + }, + "user": { + "id": "931b49af0fd64cfe8e643fa732083290", + "name": "ZSymZdG4dJkw", + "roles": [ + { + "name": "_member_" + } + ], + "roles_links": [], + "username": "ZSymZdG4dJkw" + } + } +} \ No newline at end of file diff --git a/src/corelib/OpenStack.csproj b/src/corelib/OpenStack.csproj index ac7cb0f39..8ec3eeef7 100644 --- a/src/corelib/OpenStack.csproj +++ b/src/corelib/OpenStack.csproj @@ -229,6 +229,25 @@ + + + + + + + + + + + + + + + + + + + @@ -837,6 +856,7 @@ + @@ -868,6 +888,10 @@ + + + + @@ -883,7 +907,7 @@ --> - + ..\..\packages\Flurl.Http.Signed\lib\net45\Flurl.Http.dll @@ -903,7 +927,7 @@ - + ..\..\packages\Flurl.Signed\lib\portable-net40+sl50+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Flurl.dll @@ -919,7 +943,7 @@ - + ..\..\packages\Marvin.JsonPatch.Signed\lib\portable-net40+win+wpa81\Marvin.JsonPatch.dll @@ -930,39 +954,39 @@ - + - ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.IO.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.IO.dll True True - ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Runtime.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Runtime.dll True True - ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Threading.Tasks.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Threading.Tasks.dll True True - + - ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.IO.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.IO.dll True True - ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Runtime.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Runtime.dll True True - ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Threading.Tasks.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Threading.Tasks.dll True True @@ -987,39 +1011,39 @@ - + - ..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.IO.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.IO.dll True True - ..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Runtime.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Runtime.dll True True - ..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Threading.Tasks.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Threading.Tasks.dll True True - + - ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.IO.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.IO.dll True True - ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Runtime.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Runtime.dll True True - ..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Threading.Tasks.dll + ..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Threading.Tasks.dll True True @@ -1048,13 +1072,13 @@ - - ..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.Extensions.dll + + ..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.dll True True - - ..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.dll + + ..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.Extensions.dll True True @@ -1062,13 +1086,13 @@ - - ..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.Extensions.dll + + ..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.dll True True - - ..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.dll + + ..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.Extensions.dll True True @@ -1076,13 +1100,13 @@ - - ..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.Extensions.dll + + ..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.dll True True - - ..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.dll + + ..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.Extensions.dll True True @@ -1104,29 +1128,29 @@ - + - ..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Extensions.dll + ..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Extensions.dll True True - ..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Primitives.dll + ..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Primitives.dll True True - + - ..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Extensions.dll + ..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Extensions.dll True True - ..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Primitives.dll + ..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Primitives.dll True True @@ -1134,25 +1158,25 @@ - + - ..\..\packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll + ..\..\packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll True True - + - ..\..\packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll + ..\..\packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll True True - + ..\..\packages\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll @@ -1161,7 +1185,7 @@ - + ..\..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll @@ -1170,16 +1194,16 @@ - + - ..\..\packages\Newtonsoft.Json\lib\portable-net45+wp80+win8+wpa81+dnxcore50\Newtonsoft.Json.dll + ..\..\packages\Newtonsoft.Json\lib\netcore45\Newtonsoft.Json.dll True True - + ..\..\packages\Newtonsoft.Json\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll @@ -1188,31 +1212,26 @@ - - - + - - ..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.Abstractions.dll - True - True - - - ..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.dll + + ..\..\packages\Newtonsoft.Json\lib\portable-net45+wp80+win8+wpa81+aspnetcore50\Newtonsoft.Json.dll True True + + - - ..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll + + ..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll True True - - ..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll + + ..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll True True @@ -1220,30 +1239,35 @@ - - ..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll + + ..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll True True - - ..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll + + ..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll True True - - - + - - ..\..\packages\SimpleRESTServices\lib\net35\SimpleRESTServices.dll + + ..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.dll + True + True + + + ..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.Abstractions.dll True True - + + + ..\..\packages\SimpleRESTServices\lib\net40\SimpleRESTServices.dll diff --git a/src/corelib/Serialization/CapturedStreamContent.cs b/src/corelib/Serialization/CapturedStreamContent.cs new file mode 100644 index 000000000..5109fe2dd --- /dev/null +++ b/src/corelib/Serialization/CapturedStreamContent.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.Serialization { + + /// + /// CaptureContent for stream data + /// + public class CapturedStreamContent : System.Net.Http.StreamContent { + + /// + /// Creates new CaptureContent for stream + /// + /// + public CapturedStreamContent(System.IO.Stream dataStream) : base(dataStream) + { + + } + } +} diff --git a/src/corelib/app.config b/src/corelib/app.config index 93e946be6..19027a646 100644 --- a/src/corelib/app.config +++ b/src/corelib/app.config @@ -5,6 +5,6 @@ True - + diff --git a/src/testing/integration/App.config b/src/testing/integration/App.config index 5c1fe4591..4919419b5 100644 --- a/src/testing/integration/App.config +++ b/src/testing/integration/App.config @@ -11,6 +11,6 @@ True - + \ No newline at end of file diff --git a/src/testing/integration/OpenStack.IntegrationTests.csproj b/src/testing/integration/OpenStack.IntegrationTests.csproj index 70fddbb33..a63e9e59a 100644 --- a/src/testing/integration/OpenStack.IntegrationTests.csproj +++ b/src/testing/integration/OpenStack.IntegrationTests.csproj @@ -107,31 +107,42 @@ - + - <__paket__xunit_runner_visualstudio_props>win81\xunit.runner.visualstudio - <__paket__xunit_runner_visualstudio_targets>win81\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_props>net20\xunit.runner.visualstudio - + - <__paket__xunit_runner_visualstudio_props>net20\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_props>portable-net45+win8+wp8+wpa81\xunit.runner.visualstudio - + - <__paket__xunit_runner_visualstudio_props>wpa81\xunit.runner.visualstudio - <__paket__xunit_runner_visualstudio_targets>wpa81\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_props>uap10.0\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_targets>uap10.0\xunit.runner.visualstudio - + - <__paket__xunit_runner_visualstudio_props>portable-net45+win8+wp8+wpa81\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_props>win81\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_targets>win81\xunit.runner.visualstudio + + + + + <__paket__xunit_runner_visualstudio_props>wpa81\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_targets>wpa81\xunit.runner.visualstudio + + + <__paket__xunit_core_props>portable-net45+win8+wp8+wpa81\xunit.core + + <__paket__xunit_core_props>win81\xunit.core @@ -142,11 +153,6 @@ <__paket__xunit_core_props>wpa81\xunit.core - - - <__paket__xunit_core_props>portable-net45+win8+wp8+wpa81\xunit.core - - - + ..\..\..\packages\Flurl.Http.Signed\lib\net45\Flurl.Http.dll @@ -178,7 +184,7 @@ - + ..\..\..\packages\Flurl.Signed\lib\portable-net40+sl50+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Flurl.dll @@ -189,7 +195,7 @@ - + ..\..\..\packages\Marvin.JsonPatch.Signed\lib\portable-net40+win+wpa81\Marvin.JsonPatch.dll @@ -200,39 +206,39 @@ - + - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.IO.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.IO.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Runtime.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Runtime.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Threading.Tasks.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Threading.Tasks.dll True True - + - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.IO.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.IO.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Runtime.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Runtime.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Threading.Tasks.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Threading.Tasks.dll True True @@ -257,39 +263,39 @@ - + - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.IO.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.IO.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Runtime.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Runtime.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Threading.Tasks.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Threading.Tasks.dll True True - + - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.IO.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.IO.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Runtime.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Runtime.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Threading.Tasks.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Threading.Tasks.dll True True @@ -318,13 +324,13 @@ - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.Extensions.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.dll True True - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.Extensions.dll True True @@ -332,13 +338,13 @@ - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.Extensions.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.dll True True - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.Extensions.dll True True @@ -346,13 +352,13 @@ - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.Extensions.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.dll True True - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.Extensions.dll True True @@ -374,29 +380,29 @@ - + - ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Extensions.dll + ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Extensions.dll True True - ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Primitives.dll + ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Primitives.dll True True - + - ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Extensions.dll + ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Extensions.dll True True - ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Primitives.dll + ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Primitives.dll True True @@ -413,7 +419,7 @@ - + ..\..\..\packages\Moq\lib\net40\Moq.dll @@ -433,25 +439,25 @@ - + - ..\..\..\packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll + ..\..\..\packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll True True - + - ..\..\..\packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll + ..\..\..\packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll True True - + ..\..\..\packages\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll @@ -460,7 +466,7 @@ - + ..\..\..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll @@ -469,16 +475,16 @@ - + - ..\..\..\packages\Newtonsoft.Json\lib\portable-net45+wp80+win8+wpa81+dnxcore50\Newtonsoft.Json.dll + ..\..\..\packages\Newtonsoft.Json\lib\netcore45\Newtonsoft.Json.dll True True - + ..\..\..\packages\Newtonsoft.Json\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll @@ -487,31 +493,26 @@ - - - + - - ..\..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.Abstractions.dll - True - True - - - ..\..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.dll + + ..\..\..\packages\Newtonsoft.Json\lib\portable-net45+wp80+win8+wpa81+aspnetcore50\Newtonsoft.Json.dll True True + + - - ..\..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll + + ..\..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll True True - - ..\..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll + + ..\..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll True True @@ -519,13 +520,27 @@ + + ..\..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll + True + True + ..\..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll True True + + + + - ..\..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll + ..\..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.dll + True + True + + + ..\..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.Abstractions.dll True True @@ -542,7 +557,7 @@ - + ..\..\..\packages\SharpZipLib\lib\20\ICSharpCode.SharpZipLib.dll @@ -571,16 +586,7 @@ - - - - ..\..\..\packages\SimpleRESTServices\lib\net35\SimpleRESTServices.dll - True - True - - - - + ..\..\..\packages\SimpleRESTServices\lib\net40\SimpleRESTServices.dll @@ -591,7 +597,7 @@ - + ..\..\..\packages\xunit.abstractions\lib\net35\xunit.abstractions.dll @@ -600,7 +606,7 @@ - + ..\..\..\packages\xunit.abstractions\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.abstractions.dll @@ -611,7 +617,7 @@ - + ..\..\..\packages\xunit.assert\lib\portable-net45+win8+wp8+wpa81\xunit.assert.dll @@ -622,7 +628,7 @@ - + ..\..\..\packages\xunit.extensibility.core\lib\portable-net45+win8+wp8+wpa81\xunit.core.dll @@ -633,16 +639,25 @@ - + - ..\..\..\packages\xunit.extensibility.execution\lib\win8\xunit.execution.dotnet.dll + ..\..\..\packages\xunit.extensibility.execution\lib\monoandroid\xunit.execution.dotnet.dll + True + True + + + + + + + ..\..\..\packages\xunit.extensibility.execution\lib\monotouch\xunit.execution.dotnet.dll True True - + ..\..\..\packages\xunit.extensibility.execution\lib\net45\xunit.execution.desktop.dll @@ -651,19 +666,19 @@ - + - ..\..\..\packages\xunit.extensibility.execution\lib\monoandroid\xunit.execution.dotnet.dll + ..\..\..\packages\xunit.extensibility.execution\lib\portable-net45+win8+wp8+wpa81\xunit.execution.dotnet.dll True True - + - ..\..\..\packages\xunit.extensibility.execution\lib\monotouch\xunit.execution.dotnet.dll + ..\..\..\packages\xunit.extensibility.execution\lib\win8\xunit.execution.dotnet.dll True True @@ -696,15 +711,6 @@ - - - - ..\..\..\packages\xunit.extensibility.execution\lib\portable-net45+win8+wp8+wpa81\xunit.execution.dotnet.dll - True - True - - - \ No newline at end of file diff --git a/src/testing/unit/OpenStack.UnitTests.csproj b/src/testing/unit/OpenStack.UnitTests.csproj index 0c774ebc3..84b94e13d 100644 --- a/src/testing/unit/OpenStack.UnitTests.csproj +++ b/src/testing/unit/OpenStack.UnitTests.csproj @@ -118,31 +118,42 @@ - + - <__paket__xunit_runner_visualstudio_props>win81\xunit.runner.visualstudio - <__paket__xunit_runner_visualstudio_targets>win81\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_props>net20\xunit.runner.visualstudio - + - <__paket__xunit_runner_visualstudio_props>net20\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_props>portable-net45+win8+wp8+wpa81\xunit.runner.visualstudio - + - <__paket__xunit_runner_visualstudio_props>wpa81\xunit.runner.visualstudio - <__paket__xunit_runner_visualstudio_targets>wpa81\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_props>uap10.0\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_targets>uap10.0\xunit.runner.visualstudio - + - <__paket__xunit_runner_visualstudio_props>portable-net45+win8+wp8+wpa81\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_props>win81\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_targets>win81\xunit.runner.visualstudio + + + + + <__paket__xunit_runner_visualstudio_props>wpa81\xunit.runner.visualstudio + <__paket__xunit_runner_visualstudio_targets>wpa81\xunit.runner.visualstudio + + + <__paket__xunit_core_props>portable-net45+win8+wp8+wpa81\xunit.core + + <__paket__xunit_core_props>win81\xunit.core @@ -153,11 +164,6 @@ <__paket__xunit_core_props>wpa81\xunit.core - - - <__paket__xunit_core_props>portable-net45+win8+wp8+wpa81\xunit.core - - - + ..\..\..\packages\Flurl.Http.Signed\lib\net45\Flurl.Http.dll @@ -189,7 +195,7 @@ - + ..\..\..\packages\Flurl.Signed\lib\portable-net40+sl50+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Flurl.dll @@ -200,7 +206,7 @@ - + ..\..\..\packages\Marvin.JsonPatch.Signed\lib\portable-net40+win+wpa81\Marvin.JsonPatch.dll @@ -211,39 +217,39 @@ - + - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.IO.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.IO.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Runtime.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Runtime.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Threading.Tasks.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Threading.Tasks.dll True True - + - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.IO.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.IO.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Runtime.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Runtime.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8\System.Threading.Tasks.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Threading.Tasks.dll True True @@ -268,39 +274,39 @@ - + - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.IO.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.IO.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Runtime.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Runtime.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Threading.Tasks.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl5+win8+wp8+wpa81\System.Threading.Tasks.dll True True - + - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.IO.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.IO.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Runtime.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Runtime.dll True True - ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+sl4+win8+wp71+wpa81\System.Threading.Tasks.dll + ..\..\..\packages\Microsoft.Bcl\lib\portable-net40+win8\System.Threading.Tasks.dll True True @@ -329,13 +335,13 @@ - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.Extensions.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.dll True True - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.Extensions.dll True True @@ -343,13 +349,13 @@ - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.Extensions.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.dll True True - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wp8+wpa81\Microsoft.Threading.Tasks.Extensions.dll True True @@ -357,13 +363,13 @@ - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.Extensions.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.dll True True - - ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.dll + + ..\..\..\packages\Microsoft.Bcl.Async\lib\portable-net45+win8+wpa81\Microsoft.Threading.Tasks.Extensions.dll True True @@ -385,29 +391,29 @@ - + - ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Extensions.dll + ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Extensions.dll True True - ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Primitives.dll + ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Primitives.dll True True - + - ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Extensions.dll + ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Extensions.dll True True - ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8\System.Net.Http.Primitives.dll + ..\..\..\packages\Microsoft.Net.Http\lib\portable-net45+win8+wpa81\System.Net.Http.Primitives.dll True True @@ -424,7 +430,7 @@ - + ..\..\..\packages\Moq\lib\net40\Moq.dll @@ -444,25 +450,25 @@ - + - ..\..\..\packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll + ..\..\..\packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll True True - + - ..\..\..\packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll + ..\..\..\packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll True True - + ..\..\..\packages\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll @@ -471,7 +477,7 @@ - + ..\..\..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll @@ -480,16 +486,16 @@ - + - ..\..\..\packages\Newtonsoft.Json\lib\portable-net45+wp80+win8+wpa81+dnxcore50\Newtonsoft.Json.dll + ..\..\..\packages\Newtonsoft.Json\lib\netcore45\Newtonsoft.Json.dll True True - + ..\..\..\packages\Newtonsoft.Json\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll @@ -498,31 +504,26 @@ - - - + - - ..\..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.Abstractions.dll - True - True - - - ..\..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.dll + + ..\..\..\packages\Newtonsoft.Json\lib\portable-net45+wp80+win8+wpa81+aspnetcore50\Newtonsoft.Json.dll True True + + - - ..\..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll + + ..\..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll True True - - ..\..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll + + ..\..\..\packages\PCLStorage\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll True True @@ -530,30 +531,35 @@ - - ..\..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll + + ..\..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll True True - - ..\..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll + + ..\..\..\packages\PCLStorage\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll True True - - - + - - ..\..\..\packages\SimpleRESTServices\lib\net35\SimpleRESTServices.dll + + ..\..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.dll + True + True + + + ..\..\..\packages\PCLStorage\lib\portable-win8+wpa81\PCLStorage.Abstractions.dll True True - + + + ..\..\..\packages\SimpleRESTServices\lib\net40\SimpleRESTServices.dll @@ -564,7 +570,7 @@ - + ..\..\..\packages\xunit.abstractions\lib\net35\xunit.abstractions.dll @@ -573,7 +579,7 @@ - + ..\..\..\packages\xunit.abstractions\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.abstractions.dll @@ -584,7 +590,7 @@ - + ..\..\..\packages\xunit.assert\lib\portable-net45+win8+wp8+wpa81\xunit.assert.dll @@ -595,7 +601,7 @@ - + ..\..\..\packages\xunit.extensibility.core\lib\portable-net45+win8+wp8+wpa81\xunit.core.dll @@ -606,16 +612,25 @@ - + - ..\..\..\packages\xunit.extensibility.execution\lib\win8\xunit.execution.dotnet.dll + ..\..\..\packages\xunit.extensibility.execution\lib\monoandroid\xunit.execution.dotnet.dll True True - + + + + ..\..\..\packages\xunit.extensibility.execution\lib\monotouch\xunit.execution.dotnet.dll + True + True + + + + ..\..\..\packages\xunit.extensibility.execution\lib\net45\xunit.execution.desktop.dll @@ -624,19 +639,19 @@ - + - ..\..\..\packages\xunit.extensibility.execution\lib\monoandroid\xunit.execution.dotnet.dll + ..\..\..\packages\xunit.extensibility.execution\lib\portable-net45+win8+wp8+wpa81\xunit.execution.dotnet.dll True True - + - ..\..\..\packages\xunit.extensibility.execution\lib\monotouch\xunit.execution.dotnet.dll + ..\..\..\packages\xunit.extensibility.execution\lib\win8\xunit.execution.dotnet.dll True True @@ -669,15 +684,6 @@ - - - - ..\..\..\packages\xunit.extensibility.execution\lib\portable-net45+win8+wp8+wpa81\xunit.execution.dotnet.dll - True - True - - - \ No newline at end of file diff --git a/src/testing/unit/app.config b/src/testing/unit/app.config index 93e946be6..19027a646 100644 --- a/src/testing/unit/app.config +++ b/src/testing/unit/app.config @@ -5,6 +5,6 @@ True - + From 77a2e937b6bf98f28179893994a33bc8462f2849 Mon Sep 17 00:00:00 2001 From: "fspezi@xilium.it" Date: Wed, 8 Mar 2017 17:05:43 +0100 Subject: [PATCH 02/10] Developing ObjectStorage connector completed (alpha release). --- ...cessControlAllowOriginContainerMetadata.cs | 4 +- .../AccessControlMaxAgeContainerMetadata.cs | 4 +- .../ContentLengthContainerObjectMetadata.cs | 4 +- .../ContentTypeContainerObjectMetadata.cs | 4 +- .../TimestampContainerObjectMetadata.cs | 4 +- .../ObjectStorage/v1/Metadata/IMetadata.cs | 4 +- .../ObjectStorage/v1/Metadata/MetadataBase.cs | 8 +- .../v1/ObjectStorageApiBuilder.cs | 4 +- .../ObjectStorage/v1/ObjectStorageService.cs | 176 ++++++++---------- .../v1/Serialization/MetadataSerializer.cs | 71 +++++++ src/corelib/OpenStack.csproj | 1 + 11 files changed, 175 insertions(+), 109 deletions(-) create mode 100644 src/corelib/ObjectStorage/v1/Serialization/MetadataSerializer.cs diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs index 33fc8bc11..c816a1cad 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs @@ -26,8 +26,8 @@ public AccessControlAllowOriginContainerMetadata() : base("Access-Control-Allow- /// public string[] Origins { - get { return parseValue(this.Value); } - set { this.Value = serializeValue(value); } + get { return parseValue(this.MetadataValue); } + set { this.MetadataValue = serializeValue(value); } } /// diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs index 2bfbda00b..8d4a09e75 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs @@ -24,8 +24,8 @@ public AccessControlMaxAgeContainerMetadata() : base("Access-Control-Max-Age") /// public long MaxAgeSeconds { - get { return parseValue(this.Value); } - set { this.Value = serializeValue(value); } + get { return parseValue(this.MetadataValue); } + set { this.MetadataValue = serializeValue(value); } } /// diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs index e28ef71e0..7ad424226 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs @@ -26,8 +26,8 @@ public ContentLengthContainerObjectMetadata() : base("Content-Length") /// public long ContentLength { - get { return parseValue(this.Value); } - set { this.Value = serializeValue(value); } + get { return parseValue(this.MetadataValue); } + set { this.MetadataValue = serializeValue(value); } } /// diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs index 39125bc63..1ef8ddebd 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs @@ -26,8 +26,8 @@ public ContentTypeContainerObjectMetadata() : base("Content-Type") /// public string ContentType { - get { return parseValue(this.Value); } - set { this.Value = serializeValue(value); } + get { return parseValue(this.MetadataValue); } + set { this.MetadataValue = serializeValue(value); } } /// diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs index 61645a266..14a82e1f4 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs @@ -28,8 +28,8 @@ public TimestampContainerObjectMetadata() : base("X-Timestamp") /// public DateTime LastUpdate { - get { return parseValue(this.Value); } - set { this.Value = serializeValue(value); } + get { return parseValue(this.MetadataValue); } + set { this.MetadataValue = serializeValue(value); } } /// diff --git a/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs index e5039277d..dc007b8e2 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs @@ -18,9 +18,9 @@ public interface IMetadata : ISerializedKeyValuePair { string MetadataKey { get; } /// - /// Value of Metatag + /// Value of Metadata /// - new string Value { get; set; } + string MetadataValue { get; set; } } } diff --git a/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs b/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs index 994f04003..2a95a3e20 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs @@ -33,12 +33,18 @@ public MetadataBase(string metadataKey) /// Get or set value /// [JsonProperty] - public string Value { get; set; } + public string MetadataValue { get; set; } string ISerializedKeyValuePair.Key { get { return this.MetadataKey; } set { throw new InvalidOperationException(); } } + + string ISerializedKeyValuePair.Value + { + get { return this.MetadataValue; } + set { this.MetadataValue = value; } + } } } diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs b/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs index fcc248752..96c9202d9 100644 --- a/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs +++ b/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs @@ -240,7 +240,7 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider /// The path of object. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Metadata of container object + /// Metadata of object /// public virtual async Task ReadContainerObjectMetadataAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) { @@ -260,7 +260,7 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider /// The metadata to save in object. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Nothing + /// Metadata of object /// public virtual async Task SaveContainerObjectMetadataAsync(string containerId, string objectPath, ContainerObjectMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) { diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs index cf7e04677..549fe5cb2 100644 --- a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs +++ b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs @@ -1,10 +1,14 @@ using System.Collections.Generic; +using System.IO; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Flurl.Extensions; using Flurl.Http; using OpenStack.Authentication; using OpenStack.Networking.v2.Serialization; +using OpenStack.ObjectStorage.v1.Metadata.ContainerMetadata; +using OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata; using OpenStack.ObjectStorage.v1.Serialization; namespace OpenStack.ObjectStorage.v1 @@ -29,7 +33,10 @@ public ObjectStorageService(IAuthenticationProvider authenticationProvider, stri _objectStorageApiBuilder = new ObjectStorageApiBuilder(ServiceType.ObjectStorage, authenticationProvider, region, useInternalUrl); } + + #region Containers + /// public async Task> ListContainersAsync(CancellationToken cancellationToken = default(CancellationToken)) { @@ -40,18 +47,18 @@ public ObjectStorageService(IAuthenticationProvider authenticationProvider, stri } /// - public Task GetContainerContentAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + public async Task GetContainerContentAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) { - return _objectStorageApiBuilder + return await _objectStorageApiBuilder .GetContainerContentAsync(containerId, cancellationToken) .SendAsync() .ReceiveJson(); } /// - public Task CreateContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + public async Task CreateContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) { - return _objectStorageApiBuilder + return await _objectStorageApiBuilder .CreateContainerAsync(containerId, cancellationToken) .SendAsync() .ReceiveJson(); @@ -60,137 +67,118 @@ public ObjectStorageService(IAuthenticationProvider authenticationProvider, stri /// public async Task SaveContainerMetadataAsync(string containerId, ContainerMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) { - return await _objectStorageApiBuilder + var headerString = await _objectStorageApiBuilder .SaveContainerMetadataAsync(containerId, metadataCollection, cancellationToken) .SendAsync() .ReceiveString(); + + var metadataSerializer = new MetadataSerializer(); + + return new ContainerMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); } /// - public Task ReadContainerMetadataAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + public async Task ReadContainerMetadataAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) { - return _objectStorageApiBuilder + var headerString = await _objectStorageApiBuilder .ReadContainerMetadataAsync(containerId, cancellationToken) .SendAsync() .ReceiveString(); + + var metadataSerializer = new MetadataSerializer(); + + return new ContainerMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); } /// - public Task DeleteContainerAsync(Identifier networkId, CancellationToken cancellationToken = default(CancellationToken)) + public async Task DeleteContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) { - return _objectStorageApiBuilder - .DeleteContainerAsync(networkId, cancellationToken) + return await _objectStorageApiBuilder + .DeleteContainerAsync(containerId, cancellationToken) .SendAsync(); } - #endregion + + #endregion - #region Subnets - /// - public async Task> ListSubnetsAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - return await _objectStorageApiBuilder - .ListSubnetsAsync(cancellationToken) - .SendAsync() - .ReceiveJson(); - } - /// - public Task CreateSubnetAsync(SubnetCreateDefinition subnet, CancellationToken cancellationToken = default(CancellationToken)) - { - return _objectStorageApiBuilder - .CreateSubnetAsync(subnet, cancellationToken) - .SendAsync() - .ReceiveJson(); - } - /// - public async Task> CreateSubnetsAsync(IEnumerable subnets, CancellationToken cancellationToken = default(CancellationToken)) + #region Container Objects + + /// + public async Task> GetContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder - .CreateSubnetsAsync(subnets, cancellationToken) + .GetContainerObjectAsync(containerId, objectPath, cancellationToken) .SendAsync() - .ReceiveJson(); + .ReceiveJson(); } - - /// - public Task GetSubnetAsync(Identifier subnetId, CancellationToken cancellationToken = default(CancellationToken)) + + /// + public async Task UpdateContainerObjectAsync(string containerId, string objectPath, System.IO.Stream dataStream, CancellationToken cancellationToken = default(CancellationToken)) { - return _objectStorageApiBuilder - .GetSubnetAsync(subnetId, cancellationToken) + var headerString = await _objectStorageApiBuilder + .UpdateContainerObjectAsync(containerId, objectPath, dataStream, cancellationToken) .SendAsync() - .ReceiveJson(); - } + .ReceiveString(); - /// - public Task UpdateSubnetAsync(Identifier subnetId, SubnetUpdateDefinition subnet, CancellationToken cancellationToken = default(CancellationToken)) - { - return _objectStorageApiBuilder - .UpdateSubnetAsync(subnetId, subnet, cancellationToken) - .SendAsync() - .ReceiveJson(); - } + var metadataSerializer = new MetadataSerializer(); - /// - public Task DeleteSubnetAsync(Identifier subnetId, CancellationToken cancellationToken = default(CancellationToken)) - { - return _objectStorageApiBuilder - .DeleteSubnetAsync(subnetId, cancellationToken) - .SendAsync(); + return new ContainerObjectMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); } - #endregion + + /// + public async Task UpdateContainerObjectAsync(string containerId, string objectPath, string filePath, CancellationToken cancellationToken = default(CancellationToken)) + { + string headerString; - #region Ports + using (var dataStream = new System.IO.FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + headerString = await _objectStorageApiBuilder + .UpdateContainerObjectAsync(containerId,objectPath,dataStream,cancellationToken) + .SendAsync() + .ReceiveString(); + } - /// - public async Task> ListPortsAsync(PortListOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) - { - return await _objectStorageApiBuilder.ListPortsAsync(options, cancellationToken); - } + var metadataSerializer = new MetadataSerializer(); - /// - public Task CreatePortAsync(PortCreateDefinition port, CancellationToken cancellationToken = default(CancellationToken)) - { - return _objectStorageApiBuilder - .CreatePortAsync(port, cancellationToken) - .SendAsync() - .ReceiveJson(); + return new ContainerObjectMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); } - - /// - public async Task> CreatePortsAsync(IEnumerable ports, CancellationToken cancellationToken = default(CancellationToken)) + + /// + public async Task DeleteContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder - .CreatePortsAsync(ports, cancellationToken) - .SendAsync() - .ReceiveJson(); + .DeleteContainerObjectAsync(containerId, objectPath, cancellationToken) + .SendAsync(); } - - /// - public Task GetPortAsync(Identifier portId, CancellationToken cancellationToken = default(CancellationToken)) + + /// + public async Task ReadContainerObjectMetadataAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) { - return _objectStorageApiBuilder - .GetPortAsync(portId, cancellationToken) + var headerString = await _objectStorageApiBuilder + .ReadContainerObjectMetadataAsync(containerId, objectPath, cancellationToken) .SendAsync() - .ReceiveJson(); - } + .ReceiveString(); - /// - public Task UpdatePortAsync(Identifier portId, PortUpdateDefinition port, CancellationToken cancellationToken = default(CancellationToken)) + var metadataSerializer = new MetadataSerializer(); + + return new ContainerObjectMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); + } + + /// + public async Task SaveContainerObjectMetadataAsync(string containerId, string objectPath, ContainerObjectMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) { - return _objectStorageApiBuilder - .UpdatePortAsync(portId, port, cancellationToken) + var headerString = await _objectStorageApiBuilder + .SaveContainerObjectMetadataAsync(containerId, objectPath, metadataCollection, cancellationToken) .SendAsync() - .ReceiveJson(); - } + .ReceiveString(); - /// - public Task DeletePortAsync(Identifier portId, CancellationToken cancellationToken = default(CancellationToken)) - { - return _objectStorageApiBuilder - .DeletePortAsync(portId, cancellationToken) - .SendAsync(); + var metadataSerializer = new MetadataSerializer(); + + return new ContainerObjectMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); } - #endregion - } + + #endregion + } } diff --git a/src/corelib/ObjectStorage/v1/Serialization/MetadataSerializer.cs b/src/corelib/ObjectStorage/v1/Serialization/MetadataSerializer.cs new file mode 100644 index 000000000..bff2ea5f3 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Serialization/MetadataSerializer.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using OpenStack.ObjectStorage.v1.Metadata; + +namespace OpenStack.ObjectStorage.v1.Serialization { + + /// + /// Stringify and parse Metadata + /// + public class MetadataSerializer where T : IMetadata { + + /// + /// Stringify the Metadata collection. + /// + /// + /// + public string StringifyMetadataHeaderStyle(IEnumerable metadataCollection) + { + if (metadataCollection == null) return ""; + + var fReturn = new StringBuilder(); + + foreach (var metadata in metadataCollection) + { + fReturn.AppendLine(string.Format("{0}: {1}", metadata.MetadataKey, metadata.MetadataValue)); + } + + return fReturn.ToString(); + } + + /// + /// Parse value to Metadata collection + /// + /// Serialized value of Metadata collection + /// + public IEnumerable ParseMetadataHeaderStyle(string serializedValue) + { + if (string.IsNullOrEmpty(serializedValue)) yield break; + + var metadataTypeInterface = typeof(T); + var metadataTypes = System.Reflection.Assembly.GetExecutingAssembly() + .GetTypes() + .Where(classType => classType.IsClass && !classType.IsAbstract && metadataTypeInterface.IsAssignableFrom(classType)) + .Select(classType => new { type = classType, metadataKey = ((T)System.Activator.CreateInstance(classType)).MetadataKey }) + .ToArray(); + + var re = new Regex(@"\s*(?[a-zA-Z\-\_]+)\s*:\s*(?[^\n\r]*)"); + var matches = re.Matches(serializedValue); + + foreach (Match match in matches) + { + var key = match.Groups["key"].Value; + var value = match.Groups["value"].Value.Trim(); + + var metadataInfo = metadataTypes + .FirstOrDefault(item => item.metadataKey.Equals(key, StringComparison.InvariantCultureIgnoreCase)); + if (metadataInfo == null) continue; + + var metadata = (T)System.Activator.CreateInstance(metadataInfo.type); + metadata.MetadataValue = value; + + yield return metadata; + } + } + + } +} diff --git a/src/corelib/OpenStack.csproj b/src/corelib/OpenStack.csproj index 8ec3eeef7..4f9edfb67 100644 --- a/src/corelib/OpenStack.csproj +++ b/src/corelib/OpenStack.csproj @@ -248,6 +248,7 @@ + From 1e8ea64f75cee9c8533f28ceac1537278b0e23ae Mon Sep 17 00:00:00 2001 From: "fspezi@xilium.it" Date: Thu, 9 Mar 2017 17:13:15 +0100 Subject: [PATCH 03/10] ObjectStorage: implemented and tested. --- .../AuthenticatedMessageHandler.cs | 4 +- src/corelib/Authentication/ServiceType.cs | 2 +- src/corelib/Extensions/FlurlExtensions.cs | 11 + src/corelib/Flurl/PreparedRequest.cs | 77 ++- ...cessControlAllowOriginContainerMetadata.cs | 32 +- .../AccessControlMaxAgeContainerMetadata.cs | 25 +- .../ContentLengthContainerObjectMetadata.cs | 25 +- .../ContentTypeContainerObjectMetadata.cs | 30 +- .../ETagContainerObjectMetadata.cs | 34 ++ .../LastModifiedContainerObjectMetadata.cs | 35 ++ .../TimestampContainerObjectMetadata.cs | 38 +- .../ObjectStorage/v1/Metadata/IMetadata.cs | 14 +- .../ObjectStorage/v1/Metadata/MetadataBase.cs | 30 +- .../v1/Metadata/MetadataConverter.cs | 272 +++++++++++ .../v1/ObjectStorageApiBuilder.cs | 443 +++++++++--------- .../ObjectStorage/v1/ObjectStorageService.cs | 283 ++++++----- .../v1/Serialization/ContainerCollection.cs | 1 - .../ContainerMetadataCollection.cs | 11 +- .../ContainerObjectCollection.cs | 1 - .../ContainerObjectMetadataCollection.cs | 11 +- .../Serialization/ISerializedKeyValuePair.cs | 25 - .../v1/Serialization/MetadataSerializer.cs | 94 +++- .../v1/curl-tests/containerObject-head.txt | 17 + src/corelib/OpenStack.csproj | 5 +- .../ObjectStorage/v1/ObjectStorageTests.cs | 155 ++++++ src/testing/unit/OpenStack.UnitTests.csproj | 1 + 26 files changed, 1127 insertions(+), 549 deletions(-) create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ETagContainerObjectMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/LastModifiedContainerObjectMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/MetadataConverter.cs delete mode 100644 src/corelib/ObjectStorage/v1/Serialization/ISerializedKeyValuePair.cs create mode 100644 src/corelib/ObjectStorage/v1/curl-tests/containerObject-head.txt create mode 100644 src/testing/unit/ObjectStorage/v1/ObjectStorageTests.cs diff --git a/src/corelib/Authentication/AuthenticatedMessageHandler.cs b/src/corelib/Authentication/AuthenticatedMessageHandler.cs index 3d75dc6bd..178d6e092 100644 --- a/src/corelib/Authentication/AuthenticatedMessageHandler.cs +++ b/src/corelib/Authentication/AuthenticatedMessageHandler.cs @@ -29,7 +29,9 @@ protected async override Task SendAsync(HttpRequestMessage try { - return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); + var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); + var contentStr = await response.Content.ReadAsStringAsync(); + return response; } catch (FlurlHttpException ex) { diff --git a/src/corelib/Authentication/ServiceType.cs b/src/corelib/Authentication/ServiceType.cs index 89e11bec5..65251dbe8 100644 --- a/src/corelib/Authentication/ServiceType.cs +++ b/src/corelib/Authentication/ServiceType.cs @@ -88,7 +88,7 @@ public override int GetHashCode() /// /// The ObjectStorage service /// - public static readonly ServiceType ObjectStorage = new ServiceType("object-storage"); + public static readonly ServiceType ObjectStorage = new ServiceType("object-store"); } /// diff --git a/src/corelib/Extensions/FlurlExtensions.cs b/src/corelib/Extensions/FlurlExtensions.cs index 6fec955fa..5ec625f8f 100644 --- a/src/corelib/Extensions/FlurlExtensions.cs +++ b/src/corelib/Extensions/FlurlExtensions.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading.Tasks; using Flurl.Http; using OpenStack; @@ -132,5 +133,15 @@ public static async Task SendAsync(this Task + /// Sends the . + /// + /// A task with returns the response + /// + public static async Task ReceiveHeaders(this Task responseTask) + { + return await Task.FromResult(responseTask.Result.Headers); + } } } diff --git a/src/corelib/Flurl/PreparedRequest.cs b/src/corelib/Flurl/PreparedRequest.cs index c434ac959..ed5a980ed 100644 --- a/src/corelib/Flurl/PreparedRequest.cs +++ b/src/corelib/Flurl/PreparedRequest.cs @@ -1,11 +1,17 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; using System.Threading; using System.Threading.Tasks; +using Flurl.Http.Configuration; using Flurl.Http.Content; +using net.openstack.Core; +using OpenStack.ObjectStorage.v1.Metadata; using OpenStack.ObjectStorage.v1.Serialization; using OpenStack.Serialization; @@ -73,6 +79,18 @@ public PreparedRequest(Url url, bool autoDispose) : base(url, autoDispose) /// The optional canellation token which will be used in the request, defaults to None. /// public CancellationToken CancellationToken { get; protected set; } + + /// + /// Apply default timeout to current Request + /// + /// The timeout to apply. + /// + public PreparedRequest SettingTimeout(TimeSpan defaultTimeout) + { + Settings.DefaultTimeout = defaultTimeout; + return this; + } + /// /// Prepares the client to send a DELETE request @@ -124,16 +142,71 @@ public PreparedRequest(Url url, bool autoDispose) : base(url, autoDispose) CancellationToken = cancellationToken; return this; } + + /// + /// Prepares the client to send a POST request containing form + /// + public PreparedRequest PreparePostForm(IEnumerable>> formData, CancellationToken cancellationToken = default(CancellationToken)) + { + Verb = HttpMethod.Post; + + // var multipartFormDataContent = new MultipartFormDataContent(); + //multipartFormDataContent.Add(new StringContent(pairValue), pair.Key.ToLowerInvariant().Replace('-', '_')); + /*foreach (var pair in formData) + { + foreach (var pairValue in pair.Value) + { + multipartFormDataContent.Add(new StringContent(pairValue), pair.Key.ToLowerInvariant().Replace('-', '_')); + } + } + Content = multipartFormDataContent;*/ + // this.PostUrlEncodedAsync(formData, cancellationToken); + Content = new CapturedUrlEncodedContent( + Settings.UrlEncodedSerializer.Serialize( + formData.SelectMany( + pair => pair.Value, + (pair, pairVal) => new KeyValuePair(pair.Key, pairVal) + ) + ) + ); + + CancellationToken = cancellationToken; + return this; + } + + /// + /// Prepares the client to send a POST request containing data in content Header + /// + /// + /// + /// + public PreparedRequest PreparePostContentHeader(IEnumerable>> formData, CancellationToken cancellationToken = default(CancellationToken)) + { + Verb = HttpMethod.Post; + + Content = new System.Net.Http.StringContent(""); + Content.Headers.Remove("Content-Type"); + foreach (var pair in formData) + { + foreach (var pairValue in pair.Value) + { + Content.Headers.Add(pair.Key, pairValue); + } + } + + CancellationToken = cancellationToken; + return this; + } /// /// Prepares the client to send a POST request containing data in header /// - public PreparedRequest PreparePostHeader(IEnumerable data, CancellationToken cancellationToken = default(CancellationToken)) + public PreparedRequest PreparePostHeader(IEnumerable data, CancellationToken cancellationToken = default(CancellationToken)) { Verb = HttpMethod.Post; foreach (var keyValuePair in data) { - this.WithHeader(keyValuePair.Key, keyValuePair.Value); + this.HttpClient.DefaultRequestHeaders.Add(keyValuePair.MetadataKey, keyValuePair.MetadataValue); } CancellationToken = cancellationToken; return this; diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs index c816a1cad..c1a00ed13 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlAllowOriginContainerMetadata.cs @@ -16,7 +16,7 @@ public class AccessControlAllowOriginContainerMetadata : MetadataBase, IContaine /// /// Create new instance /// - public AccessControlAllowOriginContainerMetadata() : base("Access-Control-Allow-Origin") + public AccessControlAllowOriginContainerMetadata() : base("X-Container-Meta-Access-Control-Allow-Origin", true) { } @@ -26,33 +26,9 @@ public AccessControlAllowOriginContainerMetadata() : base("Access-Control-Allow- /// public string[] Origins { - get { return parseValue(this.MetadataValue); } - set { this.MetadataValue = serializeValue(value); } - } - - /// - /// Serialize value to Metadata - /// - /// - private static string serializeValue(string[] value) - { - if (value == null) return ""; - - return string.Join(" ", value); - } - - /// - /// Parse value from Metadata - /// - /// - private static string[] parseValue(string value) - { - if (string.IsNullOrEmpty(value)) - { - return null; - } - - return value.Split(new [] {' '}, StringSplitOptions.RemoveEmptyEntries); + get { return MetadataConverter.ParseStringMultiValue(this.MetadataValue); } + set { this.MetadataValue = MetadataConverter.SerializeStringValue(value); } } + } } diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs index 8d4a09e75..2bc884da9 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerMetadata/AccessControlMaxAgeContainerMetadata.cs @@ -14,7 +14,7 @@ public class AccessControlMaxAgeContainerMetadata : MetadataBase, IContainerMeta /// /// Create new instance /// - public AccessControlMaxAgeContainerMetadata() : base("Access-Control-Max-Age") + public AccessControlMaxAgeContainerMetadata() : base("X-Container-Meta-Access-Control-Max-Age", false) { } @@ -24,26 +24,9 @@ public AccessControlMaxAgeContainerMetadata() : base("Access-Control-Max-Age") /// public long MaxAgeSeconds { - get { return parseValue(this.MetadataValue); } - set { this.MetadataValue = serializeValue(value); } - } - - /// - /// Serialize value to Metadata - /// - /// - private static string serializeValue(long value) - { - return value.ToString("0"); - } - - /// - /// Parse value from Metadata - /// - /// - private static long parseValue(string value) - { - return System.Convert.ToInt64(value); + get { return MetadataConverter.ParseLongSingleValue(this.MetadataValue); } + set { this.MetadataValue = MetadataConverter.SerializeLongValue(value); } } + } } diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs index 7ad424226..7678db89f 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentLengthContainerObjectMetadata.cs @@ -16,7 +16,7 @@ public class ContentLengthContainerObjectMetadata : MetadataBase, IContainerObje /// /// Create new instance /// - public ContentLengthContainerObjectMetadata() : base("Content-Length") + public ContentLengthContainerObjectMetadata() : base("Content-Length", false) { } @@ -26,26 +26,9 @@ public ContentLengthContainerObjectMetadata() : base("Content-Length") /// public long ContentLength { - get { return parseValue(this.MetadataValue); } - set { this.MetadataValue = serializeValue(value); } - } - - /// - /// Serialize value to Metadata - /// - /// - private static string serializeValue(long value) - { - return value.ToString("0"); - } - - /// - /// Parse value from Metadata - /// - /// - private static long parseValue(string value) - { - return System.Convert.ToInt64(value); + get { return MetadataConverter.ParseLongSingleValue(this.MetadataValue); } + set { this.MetadataValue = MetadataConverter.SerializeLongValue(value); } } + } } diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs index 1ef8ddebd..01851d860 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ContentTypeContainerObjectMetadata.cs @@ -16,7 +16,7 @@ public class ContentTypeContainerObjectMetadata : MetadataBase, IContainerObject /// /// Create new instance /// - public ContentTypeContainerObjectMetadata() : base("Content-Type") + public ContentTypeContainerObjectMetadata() : base("Content-Type", false) { } @@ -26,31 +26,9 @@ public ContentTypeContainerObjectMetadata() : base("Content-Type") /// public string ContentType { - get { return parseValue(this.MetadataValue); } - set { this.MetadataValue = serializeValue(value); } - } - - /// - /// Serialize value to Metadata - /// - /// - private static string serializeValue(string value) - { - return value ?? ""; - } - - /// - /// Parse value from Metadata - /// - /// - private static string parseValue(string value) - { - if (string.IsNullOrEmpty(value)) - { - return null; - } - - return value; + get { return MetadataConverter.ParseStringSingleValue(this.MetadataValue); } + set { this.MetadataValue = MetadataConverter.SerializeStringValue(value); } } + } } diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ETagContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ETagContainerObjectMetadata.cs new file mode 100644 index 000000000..5cdc6b476 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ETagContainerObjectMetadata.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Serialization; + +namespace OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata { + + /// + /// The `Content-Type` header metadata. + /// + public class ETagContainerObjectMetadata : MetadataBase, IContainerObjectMetadata + { + + /// + /// Create new instance + /// + public ETagContainerObjectMetadata() : base("ETag", false) + { + + } + + /// + /// Get or set ETag of Object + /// + public string ETag + { + get { return MetadataConverter.ParseStringSingleValue(this.MetadataValue); } + set { this.MetadataValue = MetadataConverter.SerializeStringValue(value); } + } + + } +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/LastModifiedContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/LastModifiedContainerObjectMetadata.cs new file mode 100644 index 000000000..4fa17a90a --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/LastModifiedContainerObjectMetadata.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Serialization; + +namespace OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata { + + /// + /// The `X-Timestamp` header metadata. + /// + public class LastModifiedContainerObjectMetadata : MetadataBase, IContainerObjectMetadata + { + + /// + /// Create new instance + /// + public LastModifiedContainerObjectMetadata() : base("Last-Modified", false) + { + + } + + /// + /// Get or set LastModified of Object + /// + public DateTime LastModified + { + get { return MetadataConverter.ParseDateTimeSingleValue(this.MetadataValue); } + set { this.MetadataValue = MetadataConverter.SerializeDateTimeValue(value); } + } + + } +} diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs index 14a82e1f4..59f518d32 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs @@ -13,48 +13,34 @@ namespace OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata { public class TimestampContainerObjectMetadata : MetadataBase, IContainerObjectMetadata { - private static readonly DateTime zeroDayUnixTime = new DateTime(1970, 1, 1, 0, 0, 0, 0); + private static readonly DateTime zeroDayUnixTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); /// /// Create new instance /// - public TimestampContainerObjectMetadata() : base("X-Timestamp") + public TimestampContainerObjectMetadata() : base("X-Timestamp", false) { } - - /// - /// Get or set LastUpdate of Object - /// - public DateTime LastUpdate - { - get { return parseValue(this.MetadataValue); } - set { this.MetadataValue = serializeValue(value); } - } /// - /// Serialize value to Metadata + /// Get or set Timestamp of Object /// - /// - private static string serializeValue(DateTime value) + public double Timestamp { - return value.Subtract(zeroDayUnixTime).TotalDays.ToString("R", System.Globalization.CultureInfo.InvariantCulture); + get { return MetadataConverter.ParseDoubleSingleValue(this.MetadataValue); } + set { this.MetadataValue = MetadataConverter.SerializeDoubleValue(value); } } /// - /// Parse value from Metadata + /// Get or set Timestamp of Object in DateTime format /// - /// - private static DateTime parseValue(string value) + public DateTime TimestampDate { - if (string.IsNullOrEmpty(value)) - { - return zeroDayUnixTime; - } - - var valueDays = System.Convert.ToDouble(value, System.Globalization.CultureInfo.InvariantCulture); - - return zeroDayUnixTime.AddDays(valueDays); + get { return MetadataConverter.ParseTimestampSingleValue(this.MetadataValue); } + set { this.MetadataValue = MetadataConverter.SerializeTimestampValue(value); } } + + } } diff --git a/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs index dc007b8e2..f5f83a1fe 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/IMetadata.cs @@ -10,7 +10,7 @@ namespace OpenStack.ObjectStorage.v1.Metadata { /// /// Generic metadata /// - public interface IMetadata : ISerializedKeyValuePair { + public interface IMetadata /*: ISerializedKeyValuePair*/ { /// /// Header key of Metatag @@ -20,7 +20,17 @@ public interface IMetadata : ISerializedKeyValuePair { /// /// Value of Metadata /// - string MetadataValue { get; set; } + string[] MetadataValue { get; set; } + /// + /// If True can contains multi value, otherwise one only. + /// + bool AllowMultiValue { get; } + + /// + /// Convert Metadata to standard KeyValuePair structure + /// + /// + KeyValuePair> ToKeyValuePair(); } } diff --git a/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs b/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs index 2a95a3e20..aefba1398 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/MetadataBase.cs @@ -11,16 +11,17 @@ namespace OpenStack.ObjectStorage.v1.Metadata { /// /// Base implementation of generic Metadata /// - public abstract class MetadataBase : IMetadata, ISerializedKeyValuePair { + public abstract class MetadataBase : IMetadata { /// /// Create new . /// /// - public MetadataBase(string metadataKey) + /// + public MetadataBase(string metadataKey, bool allowMultiValue) { this.MetadataKey = metadataKey; - + this.AllowMultiValue = allowMultiValue; } /// @@ -29,22 +30,25 @@ public MetadataBase(string metadataKey) [JsonProperty] public string MetadataKey { get; } + /// + /// If True can contains multi value, otherwise one only. + /// + public bool AllowMultiValue { get; } + /// /// Get or set value /// [JsonProperty] - public string MetadataValue { get; set; } - - string ISerializedKeyValuePair.Key - { - get { return this.MetadataKey; } - set { throw new InvalidOperationException(); } - } + public string[] MetadataValue { get; set; } - string ISerializedKeyValuePair.Value + /// + /// Convert Metadata to standard KeyValuePair structure + /// + /// + public KeyValuePair> ToKeyValuePair() { - get { return this.MetadataValue; } - set { this.MetadataValue = value; } + return new KeyValuePair>(this.MetadataKey, this.MetadataValue); } + } } diff --git a/src/corelib/ObjectStorage/v1/Metadata/MetadataConverter.cs b/src/corelib/ObjectStorage/v1/Metadata/MetadataConverter.cs new file mode 100644 index 000000000..790637c9e --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/MetadataConverter.cs @@ -0,0 +1,272 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.Metadata { + + /// + /// Library of converters to/from metadata value + /// + public static class MetadataConverter { + + #region String converter + + /// + /// Serialize the string value + /// + /// + /// + public static string[] SerializeStringValue(string value) + { + if (value == null) return null; + + return new []{ value }; + } + + /// + /// Serialize the string value + /// + /// + /// + public static string[] SerializeStringValue(IEnumerable value) + { + if (value == null) return null; + + return value.ToArray(); + } + + /// + /// Parse the serialized value + /// + /// + /// + public static string ParseStringSingleValue(IEnumerable serializedValue) + { + if (serializedValue == null) return null; + + return serializedValue.FirstOrDefault(); + } + + /// + /// Parse the serialized value + /// + /// + /// + public static string[] ParseStringMultiValue(IEnumerable serializedValue) + { + if (serializedValue == null) return null; + + return serializedValue.ToArray(); + } + + #endregion + + #region Long converter + + /// + /// Serialize value to Metadata + /// + /// + public static string[] SerializeLongValue(long value) + { + return new [] { value.ToString("0", CultureInfo.InvariantCulture) }; + } + + /// + /// Serialize value to Metadata + /// + /// + public static string[] SerializeLongValue(IEnumerable value) + { + if (value == null) return null; + + return value.Select(item => item.ToString("0", CultureInfo.InvariantCulture)).ToArray(); + } + + /// + /// Parse value from Metadata + /// + /// + public static long ParseLongSingleValue(IEnumerable serializedValue) + { + if (serializedValue == null) return 0; + + var firstValue = serializedValue.FirstOrDefault(); + if (firstValue == null) return 0; + + return long.Parse(firstValue, CultureInfo.InvariantCulture); + } + + /// + /// Parse value from Metadata + /// + /// + public static long[] ParseLongMultiValue(IEnumerable serializedValue) + { + if (serializedValue == null) return null; + + return serializedValue.Select(item => long.Parse(item, CultureInfo.InvariantCulture)).ToArray(); + } + + #endregion + + #region Double converter + + /// + /// Serialize value to Metadata + /// + /// + public static string[] SerializeDoubleValue(double value) + { + return new [] { value.ToString("R", CultureInfo.InvariantCulture) }; + } + + /// + /// Serialize value to Metadata + /// + /// + public static string[] SerializeDoubleValue(IEnumerable value) + { + if (value == null) return null; + + return value.Select(item => item.ToString("R", CultureInfo.InvariantCulture)).ToArray(); + } + + /// + /// Parse value from Metadata + /// + /// + public static double ParseDoubleSingleValue(IEnumerable serializedValue) + { + if (serializedValue == null) return 0; + + var firstValue = serializedValue.FirstOrDefault(); + if (firstValue == null) return 0; + + return double.Parse(firstValue, CultureInfo.InvariantCulture); + } + + /// + /// Parse value from Metadata + /// + /// + public static double[] ParseDoubleMultiValue(IEnumerable serializedValue) + { + if (serializedValue == null) return null; + + return serializedValue.Select(item => double.Parse(item, CultureInfo.InvariantCulture)).ToArray(); + } + + #endregion + + #region DateTime converter + + /// + /// Serialize value to Metadata + /// + /// + public static string[] SerializeDateTimeValue(DateTime value) + { + return new [] { value.ToString(CultureInfo.InvariantCulture.DateTimeFormat) }; + } + + /// + /// Serialize value to Metadata + /// + /// + public static string[] SerializeDateTimeValue(IEnumerable value) + { + if (value == null) return null; + + return value.Select(item => item.ToString(CultureInfo.InvariantCulture.DateTimeFormat)).ToArray(); + } + + /// + /// Parse value from Metadata + /// + /// + public static DateTime ParseDateTimeSingleValue(IEnumerable serializedValue) + { + if (serializedValue == null) return DateTime.MinValue; + + var firstValue = serializedValue.FirstOrDefault(); + if (firstValue == null) return DateTime.MinValue; + + return DateTime.Parse(firstValue, + System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat, + DateTimeStyles.AssumeUniversal + ); + } + + /// + /// Parse value from Metadata + /// + /// + public static DateTime[] ParseDateTimeMultiValue(IEnumerable serializedValue) + { + if (serializedValue == null) return null; + + return serializedValue + .Select(item => DateTime.Parse(item, + System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat, + DateTimeStyles.AssumeUniversal + )) + .ToArray(); + } + + #endregion + + #region Timestamp converter + + private static readonly DateTime zeroDayUnixTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + + /// + /// Serialize value to Metadata + /// + /// + public static string[] SerializeTimestampValue(DateTime value) + { + return new [] { value.Subtract(zeroDayUnixTime).TotalSeconds.ToString("R", CultureInfo.InvariantCulture.DateTimeFormat) }; + } + + /// + /// Serialize value to Metadata + /// + /// + public static string[] SerializeTimestampValue(IEnumerable value) + { + if (value == null) return null; + + return value.Select(item => item.Subtract(zeroDayUnixTime).TotalSeconds.ToString("R", CultureInfo.InvariantCulture.DateTimeFormat)).ToArray(); + } + + /// + /// Parse value from Metadata + /// + /// + public static DateTime ParseTimestampSingleValue(IEnumerable serializedValue) + { + var doubleValue = ParseDoubleSingleValue(serializedValue); + + return zeroDayUnixTime.AddSeconds(doubleValue); + } + + /// + /// Parse value from Metadata + /// + /// + public static DateTime[] ParseTimestampMultiValue(IEnumerable serializedValue) + { + if (serializedValue == null) return null; + + return ParseDoubleMultiValue(serializedValue) + .Select(item => zeroDayUnixTime.AddSeconds(item)) + .ToArray(); + } + + #endregion + } +} diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs b/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs index 96c9202d9..24a085238 100644 --- a/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs +++ b/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs @@ -13,56 +13,56 @@ namespace OpenStack.ObjectStorage.v1 { - /// - /// Builds requests to the Networking API which can be further customized and then executed. - /// Intended for custom implementations. - /// - /// OpenStack Networking API v2 Reference - public class ObjectStorageApiBuilder - { - /// - protected readonly IAuthenticationProvider AuthenticationProvider; + /// + /// Builds requests to the Networking API which can be further customized and then executed. + /// Intended for custom implementations. + /// + /// OpenStack Networking API v2 Reference + public class ObjectStorageApiBuilder + { + /// + protected readonly IAuthenticationProvider AuthenticationProvider; - /// - protected readonly ServiceEndpoint Endpoint; + /// + protected readonly ServiceEndpoint Endpoint; - /// - /// Initializes a new instance of the class. - /// - /// The service type for the desired networking provider. - /// The authentication provider. - /// The region. - /// if set to true uses the internal URLs specified in the ServiceCatalog, otherwise the public URLs are used. - public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider authenticationProvider, string region, bool useInternalUrl) - { - if(serviceType == null) - throw new ArgumentNullException("serviceType"); - if (authenticationProvider == null) - throw new ArgumentNullException("authenticationProvider"); - if (string.IsNullOrEmpty(region)) - throw new ArgumentException("region cannot be null or empty", "region"); + /// + /// Initializes a new instance of the class. + /// + /// The service type for the desired networking provider. + /// The authentication provider. + /// The region. + /// if set to true uses the internal URLs specified in the ServiceCatalog, otherwise the public URLs are used. + public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider authenticationProvider, string region, bool useInternalUrl) + { + if(serviceType == null) + throw new ArgumentNullException("serviceType"); + if (authenticationProvider == null) + throw new ArgumentNullException("authenticationProvider"); + if (string.IsNullOrEmpty(region)) + throw new ArgumentException("region cannot be null or empty", "region"); - AuthenticationProvider = authenticationProvider; - Endpoint = new ServiceEndpoint(serviceType, authenticationProvider, region, useInternalUrl); - } + AuthenticationProvider = authenticationProvider; + Endpoint = new ServiceEndpoint(serviceType, authenticationProvider, region, useInternalUrl); + } - #region Tenants + #region Tenants /* - /// - /// Returns details of current Tenant. - /// - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Details of current Tenant. - /// - public async Task GetCurrentTenantAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + /// + /// Returns details of current Tenant. + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Details of current Tenant. + /// + public async Task GetCurrentTenantAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - return endpoint - .Authenticate(AuthenticationProvider) - .PrepareGet(cancellationToken); - } + return endpoint + .Authenticate(AuthenticationProvider) + .PrepareGet(cancellationToken); + } */ #endregion @@ -75,202 +75,207 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider /// A collection of containers associated with current tenant. /// public async Task ListContainersAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - return endpoint - .Authenticate(AuthenticationProvider) - .PrepareGet(cancellationToken); - } - - /// - /// Gets the container content of specified container. - /// - /// The container identifier. - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// The content of container. - /// - public virtual async Task GetContainerContentAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - - return endpoint - .AppendPathSegments(containerId) - .Authenticate(AuthenticationProvider) - .PrepareGet(cancellationToken); - } + return endpoint + .SetQueryParam("format", "json") + .Authenticate(AuthenticationProvider) + .PrepareGet(cancellationToken); + } + + /// + /// Gets the container content of specified container. + /// + /// The container identifier. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task GetContainerContentAsync(string containerName, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .SetQueryParam("format", "json") + .AppendPathSegments(containerName) + .Authenticate(AuthenticationProvider) + .PrepareGet(cancellationToken); + } + + /// + /// Gets the container content of specified container. + /// + /// The container identifier. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task CreateContainerAsync(string containerName, CancellationToken cancellationToken = default(CancellationToken)) + { + return await new Task(() => + { + throw new NotImplementedException(); + }); + } - /// - /// Gets the container content of specified container. - /// - /// The container identifier. - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// The content of container. - /// - public virtual async Task CreateContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) - { - return await new Task(() => - { - throw new NotImplementedException(); - }); - } + /// + /// Gets the metadata from container. + /// + /// The container identifier. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task ReadContainerMetadataAsync(string containerName, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - /// - /// Update metadata to container. - /// - /// The container identifier. - /// The metadata collection. - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// The content of container. - /// - public virtual async Task SaveContainerMetadataAsync(string containerId, ContainerMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + return endpoint + .AppendPathSegments(containerName) + .Authenticate(AuthenticationProvider) + .PrepareHead(cancellationToken); + } - return endpoint - .AppendPathSegments(containerId) - .Authenticate(AuthenticationProvider) - .PreparePostHeader(metadataCollection, cancellationToken); - } - - /// - /// Gets the metadata from container. - /// - /// The container identifier. - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// The content of container. - /// - public virtual async Task ReadContainerMetadataAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + /// + /// Update metadata to container. + /// + /// The container identifier. + /// The metadata collection. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task SaveContainerMetadataAsync(string containerName, ContainerMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - return endpoint - .AppendPathSegments(containerId) - .Authenticate(AuthenticationProvider) - .PrepareHead(cancellationToken); - } + return endpoint + .AppendPathSegments(containerName) + .Authenticate(AuthenticationProvider) + .PreparePostContentHeader(metadataCollection.ToKeyValuePairs(), cancellationToken); + } - /// - /// Deletes the specified container. - /// - /// The container identifier. - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - public virtual async Task DeleteContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + /// + /// Deletes the specified container. + /// + /// The container identifier. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + public virtual async Task DeleteContainerAsync(string containerName, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - return (PreparedRequest)endpoint - .AppendPathSegments(containerId) - .Authenticate(AuthenticationProvider) - .PrepareDelete(cancellationToken) - .AllowHttpStatus(HttpStatusCode.NotFound); - } + return (PreparedRequest)endpoint + .AppendPathSegments(containerName) + .Authenticate(AuthenticationProvider) + .PrepareDelete(cancellationToken) + .AllowHttpStatus(HttpStatusCode.NotFound); + } #endregion #region Container Objects - /// - /// Gets the object in specified container. - /// - /// The container identifier. - /// The path of object. - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// The content of container. - /// - public virtual async Task GetContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - - return endpoint - .AppendPathSegments(containerId, objectPath) - .Authenticate(AuthenticationProvider) - .PrepareGet(cancellationToken); - } + /// + /// Gets the object in specified container. + /// + /// The container identifier. + /// The path of object. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task GetContainerObjectAsync(string containerName, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegments(containerName, objectPath) + .SetQueryParam("format", "json") + .Authenticate(AuthenticationProvider) + .PrepareGet(cancellationToken); + } - /// - /// Add or update the object to specified container. - /// - /// The container identifier. - /// The path of object. - /// Stream to obtains content of Object - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Nothing - /// - public virtual async Task UpdateContainerObjectAsync(string containerId, string objectPath, System.IO.Stream dataStream, CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + /// + /// Add or update the object to specified container. + /// + /// The container identifier. + /// The path of object. + /// Stream to obtains content of Object + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Nothing + /// + public virtual async Task UpdateContainerObjectAsync(string containerName, string objectPath, System.IO.Stream dataStream, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - return endpoint - .AppendPathSegments(containerId, objectPath) - .Authenticate(AuthenticationProvider) - .PreparePutStream(dataStream, cancellationToken); - } + return endpoint + .AppendPathSegments(containerName, objectPath) + .Authenticate(AuthenticationProvider) + .SettingTimeout(new TimeSpan(1, 0, 0, 0, 0)) + .PreparePutStream(dataStream, cancellationToken); + } - /// - /// Delete the object in specified container. - /// - /// The container identifier. - /// The path of object. - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Nothing - /// - public virtual async Task DeleteContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + /// + /// Delete the object in specified container. + /// + /// The container identifier. + /// The path of object. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Nothing + /// + public virtual async Task DeleteContainerObjectAsync(string containerName, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - return endpoint - .AppendPathSegments(containerId, objectPath) - .Authenticate(AuthenticationProvider) - .PrepareDelete(cancellationToken); - } + return endpoint + .AppendPathSegments(containerName, objectPath) + .Authenticate(AuthenticationProvider) + .PrepareDelete(cancellationToken); + } - /// - /// Get the metadata of object in specified container. - /// - /// The container identifier. - /// The path of object. - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Metadata of object - /// - public virtual async Task ReadContainerObjectMetadataAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + /// + /// Get the metadata of object in specified container. + /// + /// The container identifier. + /// The path of object. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Metadata of object + /// + public virtual async Task ReadContainerObjectMetadataAsync(string containerName, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - return endpoint - .AppendPathSegments(containerId, objectPath) - .Authenticate(AuthenticationProvider) - .PrepareHead(cancellationToken); - } + return endpoint + .AppendPathSegments(containerName, objectPath) + .Authenticate(AuthenticationProvider) + .PrepareHead(cancellationToken) + .AllowHttpStatus(HttpStatusCode.NotFound); + } - /// - /// Save the metadata of object in specified container. - /// - /// The container identifier. - /// The path of object. - /// The metadata to save in object. - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Metadata of object - /// - public virtual async Task SaveContainerObjectMetadataAsync(string containerId, string objectPath, ContainerObjectMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) - { - Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + /// + /// Save the metadata of object in specified container. + /// + /// The container identifier. + /// The path of object. + /// The metadata to save in object. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Metadata of object + /// + public virtual async Task SaveContainerObjectMetadataAsync(string containerName, string objectPath, ContainerObjectMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - return endpoint - .AppendPathSegments(containerId, objectPath) - .Authenticate(AuthenticationProvider) - .PreparePostHeader(metadataCollection, cancellationToken); - } + return endpoint + .AppendPathSegments(containerName, objectPath) + .Authenticate(AuthenticationProvider) + .PreparePostContentHeader(metadataCollection.ToKeyValuePairs(), cancellationToken); + } #endregion diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs index 549fe5cb2..b9424c261 100644 --- a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs +++ b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs @@ -1,6 +1,9 @@ using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Flurl.Extensions; @@ -13,90 +16,85 @@ namespace OpenStack.ObjectStorage.v1 { - /// - /// The OpenStack Networking Service. - /// - /// OpenStack Networking API v2 Overview - /// OpenStack Networking API v2 Reference - public class ObjectStorageService - { - internal readonly ObjectStorageApiBuilder _objectStorageApiBuilder; - - /// - /// Initializes a new instance of the class. - /// - /// The authentication provider. - /// The region. - /// if set to true uses the internal URLs specified in the ServiceCatalog, otherwise the public URLs are used. - public ObjectStorageService(IAuthenticationProvider authenticationProvider, string region, bool useInternalUrl = false) - { - _objectStorageApiBuilder = new ObjectStorageApiBuilder(ServiceType.ObjectStorage, authenticationProvider, region, useInternalUrl); - } - - - - #region Containers - - /// - public async Task> ListContainersAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - return await _objectStorageApiBuilder - .ListContainersAsync(cancellationToken) - .SendAsync() - .ReceiveJson(); - } - - /// - public async Task GetContainerContentAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) - { - return await _objectStorageApiBuilder - .GetContainerContentAsync(containerId, cancellationToken) - .SendAsync() - .ReceiveJson(); - } - - /// - public async Task CreateContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) - { - return await _objectStorageApiBuilder - .CreateContainerAsync(containerId, cancellationToken) - .SendAsync() - .ReceiveJson(); - } - - /// - public async Task SaveContainerMetadataAsync(string containerId, ContainerMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) - { - var headerString = await _objectStorageApiBuilder - .SaveContainerMetadataAsync(containerId, metadataCollection, cancellationToken) - .SendAsync() - .ReceiveString(); + /// + /// The OpenStack Networking Service. + /// + /// OpenStack Networking API v2 Overview + /// OpenStack Networking API v2 Reference + public class ObjectStorageService + { + internal readonly ObjectStorageApiBuilder _objectStorageApiBuilder; + + /// + /// Initializes a new instance of the class. + /// + /// The authentication provider. + /// The region. + /// if set to true uses the internal URLs specified in the ServiceCatalog, otherwise the public URLs are used. + public ObjectStorageService(IAuthenticationProvider authenticationProvider, string region, bool useInternalUrl = false) + { + _objectStorageApiBuilder = new ObjectStorageApiBuilder(ServiceType.ObjectStorage, authenticationProvider, region, useInternalUrl); + } + + + + #region Containers + + /// + public async Task> ListContainersAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .ListContainersAsync(cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public async Task GetContainerContentAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .GetContainerContentAsync(containerId, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public async Task CreateContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .CreateContainerAsync(containerId, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + + /// + public async Task ReadContainerMetadataAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + var requestHeaders = await _objectStorageApiBuilder + .ReadContainerMetadataAsync(containerId, cancellationToken) + .SendAsync() + .ReceiveHeaders(); var metadataSerializer = new MetadataSerializer(); - return new ContainerMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); - } - - /// - public async Task ReadContainerMetadataAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) - { - var headerString = await _objectStorageApiBuilder - .ReadContainerMetadataAsync(containerId, cancellationToken) - .SendAsync() - .ReceiveString(); - - var metadataSerializer = new MetadataSerializer(); - - return new ContainerMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); - } - - /// - public async Task DeleteContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) - { - return await _objectStorageApiBuilder - .DeleteContainerAsync(containerId, cancellationToken) - .SendAsync(); - } + return new ContainerMetadataCollection(metadataSerializer.ParseMetadataHeaders(requestHeaders)); + } + + /// + public async Task SaveContainerMetadataAsync(string containerId, ContainerMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) + { + await _objectStorageApiBuilder + .SaveContainerMetadataAsync(containerId, metadataCollection, cancellationToken) + .SendAsync(); + } + + /// + public async Task DeleteContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .DeleteContainerAsync(containerId, cancellationToken) + .SendAsync(); + } #endregion @@ -105,79 +103,78 @@ public ObjectStorageService(IAuthenticationProvider authenticationProvider, stri #region Container Objects - /// - public async Task> GetContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) - { - return await _objectStorageApiBuilder - .GetContainerObjectAsync(containerId, objectPath, cancellationToken) - .SendAsync() - .ReceiveJson(); - } + /// + public async Task> GetContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .GetContainerObjectAsync(containerId, objectPath, cancellationToken) + .SendAsync() + .ReceiveJson(); + } - /// - public async Task UpdateContainerObjectAsync(string containerId, string objectPath, System.IO.Stream dataStream, CancellationToken cancellationToken = default(CancellationToken)) - { - var headerString = await _objectStorageApiBuilder - .UpdateContainerObjectAsync(containerId, objectPath, dataStream, cancellationToken) - .SendAsync() - .ReceiveString(); - - var metadataSerializer = new MetadataSerializer(); - - return new ContainerObjectMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); - } + /// + public async Task UpdateContainerObjectAsync(string containerId, string objectPath, System.IO.Stream dataStream, CancellationToken cancellationToken = default(CancellationToken)) + { + await _objectStorageApiBuilder + .UpdateContainerObjectAsync(containerId, objectPath, dataStream, cancellationToken) + .SendAsync(); + } - /// - public async Task UpdateContainerObjectAsync(string containerId, string objectPath, string filePath, CancellationToken cancellationToken = default(CancellationToken)) - { - string headerString; - + /// + public async Task UpdateContainerObjectAsync(string containerId, string objectPath, string filePath, CancellationToken cancellationToken = default(CancellationToken)) + { using (var dataStream = new System.IO.FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - headerString = await _objectStorageApiBuilder - .UpdateContainerObjectAsync(containerId,objectPath,dataStream,cancellationToken) - .SendAsync() - .ReceiveString(); - } - - var metadataSerializer = new MetadataSerializer(); - - return new ContainerObjectMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); - } + { + await this.UpdateContainerObjectAsync(containerId, objectPath, dataStream, cancellationToken); + } + } - /// - public async Task DeleteContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) - { - return await _objectStorageApiBuilder - .DeleteContainerObjectAsync(containerId, objectPath, cancellationToken) - .SendAsync(); - } + /// + public async Task DeleteContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + { + await _objectStorageApiBuilder + .DeleteContainerObjectAsync(containerId, objectPath, cancellationToken) + .SendAsync(); + } - /// - public async Task ReadContainerObjectMetadataAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) - { - var headerString = await _objectStorageApiBuilder - .ReadContainerObjectMetadataAsync(containerId, objectPath, cancellationToken) - .SendAsync() - .ReceiveString(); + /// + public async Task ReadContainerObjectMetadataAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + { + // Note: Object metadata are inside both `request.Content.Headers` and `request.Headers`. + + var request = await _objectStorageApiBuilder + .ReadContainerObjectMetadataAsync(containerId, objectPath, cancellationToken) + .SendAsync(); + + var requestHeaders = new List>>(); + + requestHeaders.AddRange(request.Headers.Select(item => item)); + requestHeaders.AddRange(request.Content.Headers.Select(item => item)); var metadataSerializer = new MetadataSerializer(); - return new ContainerObjectMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); - } + return new ContainerObjectMetadataCollection(metadataSerializer.ParseMetadataHeaders(requestHeaders)); + } - /// - public async Task SaveContainerObjectMetadataAsync(string containerId, string objectPath, ContainerObjectMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) - { - var headerString = await _objectStorageApiBuilder - .SaveContainerObjectMetadataAsync(containerId, objectPath, metadataCollection, cancellationToken) - .SendAsync() - .ReceiveString(); + /// + public async Task CheckContainerObjectExistsAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + { + var request = await _objectStorageApiBuilder + .ReadContainerObjectMetadataAsync(containerId, objectPath, cancellationToken) + .SendAsync(); - var metadataSerializer = new MetadataSerializer(); + var statusCode = request.StatusCode; - return new ContainerObjectMetadataCollection(metadataSerializer.ParseMetadataHeaderStyle(headerString)); - } + return statusCode == HttpStatusCode.OK; + } + + /// + public async Task SaveContainerObjectMetadataAsync(string containerId, string objectPath, ContainerObjectMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) + { + await _objectStorageApiBuilder + .SaveContainerObjectMetadataAsync(containerId, objectPath, metadataCollection, cancellationToken) + .SendAsync(); + } #endregion } diff --git a/src/corelib/ObjectStorage/v1/Serialization/ContainerCollection.cs b/src/corelib/ObjectStorage/v1/Serialization/ContainerCollection.cs index 9c9bb4a79..c06dec520 100644 --- a/src/corelib/ObjectStorage/v1/Serialization/ContainerCollection.cs +++ b/src/corelib/ObjectStorage/v1/Serialization/ContainerCollection.cs @@ -9,7 +9,6 @@ namespace OpenStack.ObjectStorage.v1.Serialization /// /// /// - [JsonConverterWithConstructor(typeof(RootWrapperConverter), "containers")] public class ContainerCollection : List { /// diff --git a/src/corelib/ObjectStorage/v1/Serialization/ContainerMetadataCollection.cs b/src/corelib/ObjectStorage/v1/Serialization/ContainerMetadataCollection.cs index ba5f80302..7e7e1e573 100644 --- a/src/corelib/ObjectStorage/v1/Serialization/ContainerMetadataCollection.cs +++ b/src/corelib/ObjectStorage/v1/Serialization/ContainerMetadataCollection.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using OpenStack.ObjectStorage.v1.Metadata; using OpenStack.ObjectStorage.v1.Metadata.ContainerMetadata; using OpenStack.Serialization; @@ -10,7 +11,6 @@ namespace OpenStack.ObjectStorage.v1.Serialization /// /// /// - [JsonConverterWithConstructor(typeof(RootWrapperConverter), "containerMetadataList")] public class ContainerMetadataCollection : List { /// @@ -27,5 +27,14 @@ public ContainerMetadataCollection() public ContainerMetadataCollection(IEnumerable containerMetadataList) : base(containerMetadataList) { } + + /// + /// Convert list to standard KeyValuePair enumerator + /// + /// + public IEnumerable>> ToKeyValuePairs() + { + return this.Select(item => item.ToKeyValuePair()); + } } } \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs b/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs index 03014b208..143482aa1 100644 --- a/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs +++ b/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs @@ -9,7 +9,6 @@ namespace OpenStack.ObjectStorage.v1.Serialization /// /// /// - [JsonConverterWithConstructor(typeof(RootWrapperConverter), "containerObjects")] public class ContainerObjectCollection : List { /// diff --git a/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectMetadataCollection.cs b/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectMetadataCollection.cs index d71f73eb6..2acb90f3e 100644 --- a/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectMetadataCollection.cs +++ b/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectMetadataCollection.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using OpenStack.ObjectStorage.v1.Metadata; using OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata; using OpenStack.Serialization; @@ -10,7 +11,6 @@ namespace OpenStack.ObjectStorage.v1.Serialization /// /// /// - [JsonConverterWithConstructor(typeof(RootWrapperConverter), "metadataList")] public class ContainerObjectMetadataCollection : List { /// @@ -27,5 +27,14 @@ public ContainerObjectMetadataCollection() public ContainerObjectMetadataCollection(IEnumerable metadataList) : base(metadataList) { } + + /// + /// Convert list to standard KeyValuePair enumerator + /// + /// + public IEnumerable>> ToKeyValuePairs() + { + return this.Select(item => item.ToKeyValuePair()); + } } } \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/Serialization/ISerializedKeyValuePair.cs b/src/corelib/ObjectStorage/v1/Serialization/ISerializedKeyValuePair.cs deleted file mode 100644 index cc5118c44..000000000 --- a/src/corelib/ObjectStorage/v1/Serialization/ISerializedKeyValuePair.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace OpenStack.ObjectStorage.v1.Serialization { - - /// - /// Represents a generic serialized collection's item - /// - public interface ISerializedKeyValuePair { - - /// - /// Key of item - /// - string Key { get; set; } - - /// - /// Value of item - /// - string Value { get; set; } - - } -} diff --git a/src/corelib/ObjectStorage/v1/Serialization/MetadataSerializer.cs b/src/corelib/ObjectStorage/v1/Serialization/MetadataSerializer.cs index bff2ea5f3..cb9ccc10c 100644 --- a/src/corelib/ObjectStorage/v1/Serialization/MetadataSerializer.cs +++ b/src/corelib/ObjectStorage/v1/Serialization/MetadataSerializer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http.Headers; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -32,6 +33,7 @@ public string StringifyMetadataHeaderStyle(IEnumerable metadataCollection) return fReturn.ToString(); } + /// /// Parse value to Metadata collection /// @@ -39,33 +41,93 @@ public string StringifyMetadataHeaderStyle(IEnumerable metadataCollection) /// public IEnumerable ParseMetadataHeaderStyle(string serializedValue) { - if (string.IsNullOrEmpty(serializedValue)) yield break; + if (string.IsNullOrEmpty(serializedValue)) return Enumerable.Empty(); + + var re = new Regex(@"\s*(?[a-zA-Z\-_]+)\s*:\s*(?[^\n\r]*)"); + var matches = re.Matches(serializedValue); - var metadataTypeInterface = typeof(T); - var metadataTypes = System.Reflection.Assembly.GetExecutingAssembly() - .GetTypes() - .Where(classType => classType.IsClass && !classType.IsAbstract && metadataTypeInterface.IsAssignableFrom(classType)) - .Select(classType => new { type = classType, metadataKey = ((T)System.Activator.CreateInstance(classType)).MetadataKey }) - .ToArray(); + return this.ParseMetadataHeaders( + matches + .Cast() + .Select(match => new KeyValuePair>( + match.Groups["key"].Value, + match.Groups["value"].Value + .Trim() + .Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(item => item.Trim()) + .Where(item => string.IsNullOrEmpty(item) == false) + )) + ); + } - var re = new Regex(@"\s*(?[a-zA-Z\-\_]+)\s*:\s*(?[^\n\r]*)"); - var matches = re.Matches(serializedValue); + /// + /// Parse HttpResponseHeaders to Metadata collection + /// + /// + /// + public IEnumerable ParseMetadataHeaders(HttpResponseHeaders headers) + { + return this.ParseMetadataHeaders(headers.Select(item => item)); + } - foreach (Match match in matches) - { - var key = match.Groups["key"].Value; - var value = match.Groups["value"].Value.Trim(); + /// + /// Parse value to Metadata collection + /// + /// + /// + public IEnumerable ParseMetadataHeaders(IEnumerable>> headers) + { + var metadataTypes = this.getCompatibleMetadataTypeList().ToArray(); + foreach (var header in headers) + { var metadataInfo = metadataTypes - .FirstOrDefault(item => item.metadataKey.Equals(key, StringComparison.InvariantCultureIgnoreCase)); + .FirstOrDefault(item => item.MetadataKey.Equals(header.Key, StringComparison.InvariantCultureIgnoreCase)); if (metadataInfo == null) continue; - var metadata = (T)System.Activator.CreateInstance(metadataInfo.type); - metadata.MetadataValue = value; + var metadata = (T)System.Activator.CreateInstance(metadataInfo.MetadataType); + + if (metadata.AllowMultiValue == false) + { + metadata.MetadataValue = header.Value + .Select(item => item.Trim()) + .Where(item => string.IsNullOrEmpty(item) == false) + .ToArray(); + } + else + { + metadata.MetadataValue = header.Value + .SelectMany(item => item.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries)) + .Select(item => item.Trim()) + .Where(item => string.IsNullOrEmpty(item) == false) + .ToArray(); + } yield return metadata; } } + + private IEnumerable getCompatibleMetadataTypeList() + { + var metadataTypeInterface = typeof(T); + var metadataTypes = System.Reflection.Assembly.GetCallingAssembly() + .GetTypes() + .Where(classType => classType.IsClass && !classType.IsAbstract && metadataTypeInterface.IsAssignableFrom(classType)) + .Select(classType => new MetadataInfo() + { + MetadataType = classType, + MetadataKey = ((T) System.Activator.CreateInstance(classType)).MetadataKey + }); + return metadataTypes; + } + + private class MetadataInfo + { + + public Type MetadataType { get; set; } + + public string MetadataKey { get; set; } + } } } diff --git a/src/corelib/ObjectStorage/v1/curl-tests/containerObject-head.txt b/src/corelib/ObjectStorage/v1/curl-tests/containerObject-head.txt new file mode 100644 index 000000000..766ef1738 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/curl-tests/containerObject-head.txt @@ -0,0 +1,17 @@ +# Guida +https://developer.openstack.org/api-ref/object-storage/?expanded=show-object-metadata-detail + +# Chiamata: +curl -i https://storage.sbg1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d/fs-test/test/metadata/my-file.txt --head -H "Content-Type: application/json" -H "X-Auth-Token: e47518f2bc504d9e805c7f52734776d2" + +# Output: +HTTP/1.1 200 OK +Content-Length: 10 +Accept-Ranges: bytes +Last-Modified: Thu, 09 Mar 2017 01:29:33 GMT +Etag: 781e5e245d69b566979b86e28d23f2c7 +X-Timestamp: 1489022972.40503 +Content-Type: application/json; charset=utf-8 +X-Trans-Id: txbd9ea0c589394d2e9cef0-0058c0b0d0 +X-Openstack-Request-Id: txbd9ea0c589394d2e9cef0-0058c0b0d0 +Date: Thu, 09 Mar 2017 01:33:04 GMT \ No newline at end of file diff --git a/src/corelib/OpenStack.csproj b/src/corelib/OpenStack.csproj index 4f9edfb67..134c4f450 100644 --- a/src/corelib/OpenStack.csproj +++ b/src/corelib/OpenStack.csproj @@ -235,19 +235,21 @@ + + + - @@ -889,6 +891,7 @@ + diff --git a/src/testing/unit/ObjectStorage/v1/ObjectStorageTests.cs b/src/testing/unit/ObjectStorage/v1/ObjectStorageTests.cs new file mode 100644 index 000000000..ca7b37f4b --- /dev/null +++ b/src/testing/unit/ObjectStorage/v1/ObjectStorageTests.cs @@ -0,0 +1,155 @@ +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenStack.Networking.v2.Serialization; +using OpenStack.ObjectStorage.v1.Serialization; +using OpenStack.Synchronous; +using OpenStack.Testing; +using OpenStackNet.Testing.Unit; +using Xunit; +using Assert = Xunit.Assert; + +namespace OpenStack.ObjectStorage.v1 +{ + [TestClass] + public class ObjectStorageTests + { + private readonly ObjectStorageService _objectStorageService; + + public ObjectStorageTests() + { + _objectStorageService = new ObjectStorageService(Stubs.AuthenticationProvider, "region"); + } + + [TestMethod] + [TestCategory(TestCategories.Unit)] + public async Task ObjectStorageTests_ListNetworks() + { + using (var httpTest = new HttpTest()) + { + var sourceContainers = new ContainerCollection(new[] + { + new Container() {Name = "www", Bytes = 1003, Count = 3}, + new Container() {Name = "media", Bytes = 1005, Count = 5} + }); + httpTest.RespondWithJson(sourceContainers); + + var containers = await _objectStorageService.ListContainersAsync(); + + httpTest.ShouldHaveCalled("*"); + Assert.NotNull(containers); + Assert.Equal(sourceContainers.Count, containers.Count()); + Assert.Equal(sourceContainers[0].Name, containers.First().Name); + Assert.Equal(sourceContainers[0].Bytes, containers.First().Bytes); + Assert.Equal(sourceContainers[0].Count, containers.First().Count); + } + } + + + /* + [TestMethod] + [TestCategory(TestCategories.Unit)] + public void CreateNetwork() + { + using (var httpTest = new HttpTest()) + { + Identifier networkId = Guid.NewGuid(); + httpTest.RespondWithJson(new Network{Id = networkId}); + + var definition = new NetworkDefinition(); + var network = _objectStorageService.CreateNetwork(definition); + + httpTest.ShouldHaveCalled("/networks"); + Assert.NotNull(network); + Assert.Equal(networkId, network.Id); + } + } + + [TestMethod] + [TestCategory(TestCategories.Unit)] + public void CreateNetworks() + { + using (var httpTest = new HttpTest()) + { + httpTest.RespondWithJson(new NetworkCollection(new[] { new Network { Name = "network-1"}, new Network{Name = "network-2"} })); + + var definitions = new[] {new NetworkDefinition(), new NetworkDefinition()}; + var networks = _objectStorageService.CreateNetworks(definitions); + + httpTest.ShouldHaveCalled("/networks"); + Assert.NotNull(networks); + Assert.Equal(2, networks.Count()); + Assert.Equal("network-1", networks.First().Name); + Assert.Equal("network-2", networks.Last().Name); + } + } + + [TestMethod] + [TestCategory(TestCategories.Unit)] + public void GetNetwork() + { + using (var httpTest = new HttpTest()) + { + Identifier networkId = Guid.NewGuid(); + httpTest.RespondWithJson(new Network { Id = networkId }); + + var network = _objectStorageService.GetNetwork(networkId); + + httpTest.ShouldHaveCalled("/networks/" + networkId); + Assert.NotNull(network); + Assert.Equal(networkId, network.Id); + } + } + + [TestMethod] + [TestCategory(TestCategories.Unit)] + public void DeleteNetwork() + { + using (var httpTest = new HttpTest()) + { + Identifier networkId = Guid.NewGuid(); + httpTest.RespondWith((int)HttpStatusCode.NoContent, "All gone!"); + + _objectStorageService.DeleteNetwork(networkId); + + httpTest.ShouldHaveCalled("/networks/" + networkId); + } + } + + [TestMethod] + [TestCategory(TestCategories.Unit)] + public void WhenDeleteNetwork_Returns404NotFound_ShouldConsiderRequestSuccessful() + { + using (var httpTest = new HttpTest()) + { + Identifier networkId = Guid.NewGuid(); + httpTest.RespondWith((int)HttpStatusCode.NotFound, "Not here, boss..."); + + _objectStorageService.DeleteNetwork(networkId); + + httpTest.ShouldHaveCalled("/networks/" + networkId); + } + } + + [TestMethod] + [TestCategory(TestCategories.Unit)] + public void UpdateNetwork() + { + using (var httpTest = new HttpTest()) + { + Identifier networkId = Guid.NewGuid(); + httpTest.RespondWithJson(new Network {Id = networkId}); + + var definition = new NetworkDefinition { Name = "new network name" }; + var network = _objectStorageService.UpdateNetwork(networkId, definition); + + httpTest.ShouldHaveCalled("/networks/" + networkId); + Assert.NotNull(network); + Assert.Equal(networkId, network.Id); + } + } + */ + } +} diff --git a/src/testing/unit/OpenStack.UnitTests.csproj b/src/testing/unit/OpenStack.UnitTests.csproj index 84b94e13d..7c98d0537 100644 --- a/src/testing/unit/OpenStack.UnitTests.csproj +++ b/src/testing/unit/OpenStack.UnitTests.csproj @@ -85,6 +85,7 @@ + From 5da35ff310e2707a7a7b515c7156f9e3bce209a9 Mon Sep 17 00:00:00 2001 From: "fspezi@xilium.it" Date: Thu, 9 Mar 2017 17:25:13 +0100 Subject: [PATCH 04/10] Removed curl tests for ObjectStorage --- .../v1/curl-tests/accounts-get.txt | 41 --- .../v1/curl-tests/containerObject-head.txt | 17 -- .../v1/curl-tests/containers-get.txt | 133 ---------- .../v1/curl-tests/tokens-noTenantId.json | 28 --- .../v1/curl-tests/tokens-withTenantId.json | 237 ------------------ 5 files changed, 456 deletions(-) delete mode 100644 src/corelib/ObjectStorage/v1/curl-tests/accounts-get.txt delete mode 100644 src/corelib/ObjectStorage/v1/curl-tests/containerObject-head.txt delete mode 100644 src/corelib/ObjectStorage/v1/curl-tests/containers-get.txt delete mode 100644 src/corelib/ObjectStorage/v1/curl-tests/tokens-noTenantId.json delete mode 100644 src/corelib/ObjectStorage/v1/curl-tests/tokens-withTenantId.json diff --git a/src/corelib/ObjectStorage/v1/curl-tests/accounts-get.txt b/src/corelib/ObjectStorage/v1/curl-tests/accounts-get.txt deleted file mode 100644 index 27afa23f7..000000000 --- a/src/corelib/ObjectStorage/v1/curl-tests/accounts-get.txt +++ /dev/null @@ -1,41 +0,0 @@ -# Guida -https://developer.openstack.org/api-ref/object-storage/?expanded=show-account-details-and-list-containers-detail - -# Chiamata: -curl -i https://storage.sbg1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d?format=json -X GET -H "X-Auth-Token: f163a4eb716d45178e6f72a8103cb711" - -# Output: -HTTP/1.1 200 OK -Content-Length: 162 -X-Account-Container-Count: 3 -X-Account-Object-Count: 28 -X-Account-Storage-Policy-Pcs-Object-Count: 28 -X-Account-Storage-Policy-Pcs-Bytes-Used: 5415613 -X-Account-Meta-Temp-Url-Key: c3S5GHmD4JKv4dYz -X-Account-Storage-Policy-Pcs-Container-Count: 3 -X-Account-Bytes-Used: 5415613 -X-Timestamp: 1435826097.48927 -Content-Type: application/json; charset=utf-8 -Accept-Ranges: bytes -x-account-project-domain-id: default -X-Trans-Id: tx6f14498ede1949ad935b2-0058bf0354 -X-Openstack-Request-Id: tx6f14498ede1949ad935b2-0058bf0354 -Date: Tue, 07 Mar 2017 19:00:36 GMT - -[ - { - "count": 8, - "bytes": 232691, - "name": "Prova hosting" - }, - { - "count": 5, - "bytes": 12517, - "name": "Prova public" - }, - { - "count": 15, - "bytes": 5170405, - "name": "fs-test" - } -] \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/curl-tests/containerObject-head.txt b/src/corelib/ObjectStorage/v1/curl-tests/containerObject-head.txt deleted file mode 100644 index 766ef1738..000000000 --- a/src/corelib/ObjectStorage/v1/curl-tests/containerObject-head.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Guida -https://developer.openstack.org/api-ref/object-storage/?expanded=show-object-metadata-detail - -# Chiamata: -curl -i https://storage.sbg1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d/fs-test/test/metadata/my-file.txt --head -H "Content-Type: application/json" -H "X-Auth-Token: e47518f2bc504d9e805c7f52734776d2" - -# Output: -HTTP/1.1 200 OK -Content-Length: 10 -Accept-Ranges: bytes -Last-Modified: Thu, 09 Mar 2017 01:29:33 GMT -Etag: 781e5e245d69b566979b86e28d23f2c7 -X-Timestamp: 1489022972.40503 -Content-Type: application/json; charset=utf-8 -X-Trans-Id: txbd9ea0c589394d2e9cef0-0058c0b0d0 -X-Openstack-Request-Id: txbd9ea0c589394d2e9cef0-0058c0b0d0 -Date: Thu, 09 Mar 2017 01:33:04 GMT \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/curl-tests/containers-get.txt b/src/corelib/ObjectStorage/v1/curl-tests/containers-get.txt deleted file mode 100644 index 86038253c..000000000 --- a/src/corelib/ObjectStorage/v1/curl-tests/containers-get.txt +++ /dev/null @@ -1,133 +0,0 @@ -# Guida -https://developer.openstack.org/api-ref/object-storage/?expanded=show-container-details-and-list-objects-detail - -# Chiamata: -curl -i https://storage.sbg1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d/fs-test?format=json -X GET -H "X-Auth-Token: f163a4eb716d45178e6f72a8103cb711" - -# Output: -HTTP/1.1 200 OK -X-Container-Meta-Web-Listings: true -Content-Length: 2490 -X-Container-Object-Count: 15 -X-Timestamp: 1488310678.00786 -X-Container-Meta-Web-Error: error.html -X-Container-Meta-Web-Listings-Css: listing.css -X-Storage-Policy: PCS -Last-Modified: Tue, 07 Mar 2017 13:56:22 GMT -X-Container-Meta-Web-Index: index.html -X-Container-Meta-Access-Control-Allow-Origin: https://www.ovh.com -X-Container-Read: .r:*,.rlistings -X-Container-Bytes-Used: 5170405 -Content-Type: application/json; charset=utf-8 -Accept-Ranges: bytes -X-Trans-Id: tx6ce0070ba54f4cd9b399d-0058bf0500 -X-Openstack-Request-Id: tx6ce0070ba54f4cd9b399d-0058bf0500 -Date: Tue, 07 Mar 2017 19:07:44 GMT - -[ - { - "hash":"84bb0c02a4a70cc3963cb17ae40ed47d", - "last_modified":"2017-02-28T19:37:58.628560", - "bytes":447, - "name":"401error.html", - "content_type":"text/html" - }, - { - "hash":"144f46132883febbd8525714c44fa1ce", - "last_modified":"2017-02-28T19:37:58.573220", - "bytes":438, - "name":"404error.html", - "content_type":"text/html" - }, - { - "hash":"1ad5d67e460e381aa5c87e5875302493", - "last_modified":"2017-02-28T19:38:00.384590", - "bytes":468, - "name":"503error.html", - "content_type":"text/html" - }, - { - "hash":"e30ce861ccc126a42a644808aba0bb43", - "last_modified":"2017-03-07T08:20:53.227720", - "bytes":2830555, - "name":"W3SVC4-2017-02.zip", - "content_type":"application/zip" - }, - { - "hash":"e52aa38131bb45c1e539b793f44acf72", - "last_modified":"2017-02-28T19:37:58.641620", - "bytes":87308, - "name":"images/401.png", - "content_type":"image/png" - }, - { - "hash":"f2fdf1aa043b12ee4b1d30d54a502619", - "last_modified":"2017-02-28T19:37:58.625040", - "bytes":43747, - "name":"images/404.png", - "content_type":"image/png" - }, - { - "hash":"bbb1d6da3f2137d19b879d9e26be95ef", - "last_modified":"2017-02-28T19:37:58.639810", - "bytes":90846, - "name":"images/503.png", - "content_type":"image/png" - }, - { - "hash":"0bbe5b868c589446365c190435918830", - "last_modified":"2017-02-28T19:37:58.635780", - "bytes":7695, - "name":"images/index.png", - "content_type":"image/png" - }, - { - "hash":"8c730d91d7a74c5aba0cd1093c63e04b", - "last_modified":"2017-02-28T19:37:58.638280", - "bytes":313, - "name":"index.html", - "content_type":"text/html" - }, - { - "hash":"e7cc89e8daeb2be27d008b1ec5ef9480", - "last_modified":"2017-02-28T19:37:58.633380", - "bytes":1876, - "name":"listing.css", - "content_type":"text/css" - }, - { - "hash":"d960cecd63061c475603c586a722aa1e", - "last_modified":"2017-02-28T19:40:15.638860", - "bytes":832995, - "name":"path1/Negozio 1.jpg", - "content_type":"image/jpeg" - }, - { - "hash":"caa10a4f0423ed70fd7ea49af2750af1", - "last_modified":"2017-02-28T19:40:12.019660", - "bytes":745340, - "name":"path1/musica-dettaglio.jpg", - "content_type":"image/jpeg" - }, - { - "hash":"3bc9754b380c0ba4c260f27d9165cbaf", - "last_modified":"2017-02-28T19:40:11.977410", - "bytes":114224, - "name":"path1/natura-tramonto.jpg", - "content_type":"image/jpeg" - }, - { - "hash":"8c1c0175fe4349810ad19c49f65eb7bc", - "last_modified":"2017-02-28T19:40:11.975840", - "bytes":414035, - "name":"path1/natura.jpg", - "content_type":"image/jpeg" - }, - { - "hash":"9467c8b0c0103dc18b9ba274c9c87abf", - "last_modified":"2017-02-28T19:37:58.629130", - "bytes":118, - "name":"styles/index.css", - "content_type":"text/css" - } -] \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/curl-tests/tokens-noTenantId.json b/src/corelib/ObjectStorage/v1/curl-tests/tokens-noTenantId.json deleted file mode 100644 index 253ab0ecc..000000000 --- a/src/corelib/ObjectStorage/v1/curl-tests/tokens-noTenantId.json +++ /dev/null @@ -1,28 +0,0 @@ -// Chiamata: -// curl -s -X POST https://auth.cloud.ovh.net/v2.0/tokens -H "Content-Type: application/json" -d '{"auth": {"tenantId":"", "passwordCredentials": {"username": "ZSymZdG4dJkw", "password": "VBXpZae2K3Ak4NDH6b2ZW4xYjYfD9EZQ"}}}' | python -m json.tool - -// Output: -{ - "access": { - "metadata": { - "is_admin": 0, - "roles": [] - }, - "serviceCatalog": [], - "token": { - "audit_ids": [ - "_siZ0-sSToWj3dueo18hbQ" - ], - "expires": "2017-03-08T18:30:43Z", - "id": "8c10f0f1259a4d899d700853afeca481", - "issued_at": "2017-03-07T18:30:43.240456" - }, - "user": { - "id": "931b49af0fd64cfe8e643fa732083290", - "name": "ZSymZdG4dJkw", - "roles": [], - "roles_links": [], - "username": "ZSymZdG4dJkw" - } - } -} \ No newline at end of file diff --git a/src/corelib/ObjectStorage/v1/curl-tests/tokens-withTenantId.json b/src/corelib/ObjectStorage/v1/curl-tests/tokens-withTenantId.json deleted file mode 100644 index 27e866c75..000000000 --- a/src/corelib/ObjectStorage/v1/curl-tests/tokens-withTenantId.json +++ /dev/null @@ -1,237 +0,0 @@ -// Chiamata: -// curl -s -X POST https://auth.cloud.ovh.net/v2.0/tokens -H "Content-Type: application/json" -d '{"auth": {"tenantId":"9aa1ef7de0284ddd9d9cf0f620a99b9d", "passwordCredentials": {"username": "ZSymZdG4dJkw", "password": "VBXpZae2K3Ak4NDH6b2ZW4xYjYfD9EZQ"}}}' | python -m json.tool - -// Output: -{ - "access": { - "metadata": { - "is_admin": 0, - "roles": [ - "9fe2ff9ee4384b1894a90878d3e92bab" - ] - }, - "serviceCatalog": [ - { - "endpoints": [ - { - "adminURL": "https://compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "id": "17f6ef1cc63e492ab8d3f2bda8428cb0", - "internalURL": "https://compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "GRA1" - }, - { - "adminURL": "https://compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "id": "21fdd202afd04470bbaf84f9396d0dcc", - "internalURL": "https://compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "BHS1" - }, - { - "adminURL": "https://compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "id": "a707bffedf1c4b80a124c585c67c1639", - "internalURL": "https://compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "SBG1" - } - ], - "endpoints_links": [], - "name": "nova", - "type": "compute" - }, - { - "endpoints": [ - { - "adminURL": "https://network.compute.gra1.cloud.ovh.net/", - "id": "26a339a8c7d5463f89ca937068ebbcd4", - "internalURL": "https://network.compute.gra1.cloud.ovh.net/", - "publicURL": "https://network.compute.gra1.cloud.ovh.net/", - "region": "GRA1" - }, - { - "adminURL": "https://network.compute.bhs1.cloud.ovh.net/", - "id": "3fe2326789ec4e37af2e6b2c80a90876", - "internalURL": "https://network.compute.bhs1.cloud.ovh.net/", - "publicURL": "https://network.compute.bhs1.cloud.ovh.net/", - "region": "BHS1" - }, - { - "adminURL": "https://network.compute.sbg1.cloud.ovh.net/", - "id": "075839111e7a41f1bb458926e5f04cec", - "internalURL": "https://network.compute.sbg1.cloud.ovh.net/", - "publicURL": "https://network.compute.sbg1.cloud.ovh.net/", - "region": "SBG1" - } - ], - "endpoints_links": [], - "name": "neutron", - "type": "network" - }, - { - "endpoints": [ - { - "adminURL": "https://volume.compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "id": "7231957fdf0346e5adebe860ac5e5e57", - "internalURL": "https://volume.compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://volume.compute.gra1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "GRA1" - }, - { - "adminURL": "https://volume.compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "id": "2f5b68f95d7b4b1fad1a683dac8e8ca3", - "internalURL": "https://volume.compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://volume.compute.bhs1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "BHS1" - }, - { - "adminURL": "https://volume.compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "id": "021b61bd7313479e8f8d77d21c7b434a", - "internalURL": "https://volume.compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://volume.compute.sbg1.cloud.ovh.net/v2/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "SBG1" - } - ], - "endpoints_links": [], - "name": "cinderv2", - "type": "volumev2" - }, - { - "endpoints": [ - { - "adminURL": "https://image.compute.gra1.cloud.ovh.net/", - "id": "56795c82f1744e47b7782f1fc2407212", - "internalURL": "https://image.compute.gra1.cloud.ovh.net/", - "publicURL": "https://image.compute.gra1.cloud.ovh.net/", - "region": "GRA1" - }, - { - "adminURL": "https://image.compute.bhs1.cloud.ovh.net/", - "id": "5eaa4cbe80354ea482f2b0477c9c16f0", - "internalURL": "https://image.compute.bhs1.cloud.ovh.net/", - "publicURL": "https://image.compute.bhs1.cloud.ovh.net/", - "region": "BHS1" - }, - { - "adminURL": "https://image.compute.sbg1.cloud.ovh.net/", - "id": "15758b246d1340e887a2170bd3399071", - "internalURL": "https://image.compute.sbg1.cloud.ovh.net/", - "publicURL": "https://image.compute.sbg1.cloud.ovh.net/", - "region": "SBG1" - } - ], - "endpoints_links": [], - "name": "glance", - "type": "image" - }, - { - "endpoints": [ - { - "adminURL": "https://volume.compute.gra1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "id": "a6936c8876c1490cbf91d0707e78d350", - "internalURL": "https://volume.compute.gra1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://volume.compute.gra1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "GRA1" - }, - { - "adminURL": "https://volume.compute.bhs1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "id": "43bc107cf78448faa9e5a6b3a5ca48dd", - "internalURL": "https://volume.compute.bhs1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://volume.compute.bhs1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "BHS1" - }, - { - "adminURL": "https://volume.compute.sbg1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "id": "2be04ee1ddb148c19e91d3da5934fa55", - "internalURL": "https://volume.compute.sbg1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://volume.compute.sbg1.cloud.ovh.net/v1/9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "SBG1" - } - ], - "endpoints_links": [], - "name": "cinder", - "type": "volume" - }, - { - "endpoints": [ - { - "adminURL": "https://storage.gra1.cloud.ovh.net", - "id": "c96f61d071a74e36bd3c07e53d241ce3", - "internalURL": "http://127.0.0.1:8888/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://storage.gra1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "GRA1" - }, - { - "adminURL": "https://storage.bhs1.cloud.ovh.net:8888/", - "id": "3327534a1a824389aae5d663b9821d67", - "internalURL": "http://127.0.0.1:8888/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://storage.bhs1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "BHS1" - }, - { - "adminURL": "https://storage.sbg1.cloud.ovh.net", - "id": "2af96b87ad484cb7879a9ea554d5418c", - "internalURL": "http://127.0.0.1:8888/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", - "publicURL": "https://storage.sbg1.cloud.ovh.net/v1/AUTH_9aa1ef7de0284ddd9d9cf0f620a99b9d", - "region": "SBG1" - } - ], - "endpoints_links": [], - "name": "swift", - "type": "object-store" - }, - { - "endpoints": [ - { - "adminURL": "https://auth.cloud.ovh.net:35357/v2.0", - "id": "62101e498fc3404dbc18ec80888992cb", - "internalURL": "http://127.0.0.1:5000/v2.0", - "publicURL": "https://auth.cloud.ovh.net/v2.0", - "region": "GRA1" - }, - { - "adminURL": "https://auth.cloud.ovh.net:35357/v2.0", - "id": "00e403276b3246c4a5c54dc7133f9f0a", - "internalURL": "http://127.0.0.1:5000/v2.0", - "publicURL": "https://auth.cloud.ovh.net/v2.0", - "region": "BHS1" - }, - { - "adminURL": "https://auth.cloud.ovh.net:35357/v2.0", - "id": "6094ef2ed9f240ed9b648dfcc0d9f923", - "internalURL": "http://127.0.0.1:5000/v2.0", - "publicURL": "https://auth.cloud.ovh.net/v2.0", - "region": "SBG1" - } - ], - "endpoints_links": [], - "name": "keystone", - "type": "identity" - } - ], - "token": { - "audit_ids": [ - "LevRPD9ZQtWhK5Ejw4_gBA" - ], - "expires": "2017-03-08T18:33:38Z", - "id": "f163a4eb716d45178e6f72a8103cb711", - "issued_at": "2017-03-07T18:33:38.344366", - "tenant": { - "description": null, - "enabled": true, - "id": "9aa1ef7de0284ddd9d9cf0f620a99b9d", - "name": "9433942458871390" - } - }, - "user": { - "id": "931b49af0fd64cfe8e643fa732083290", - "name": "ZSymZdG4dJkw", - "roles": [ - { - "name": "_member_" - } - ], - "roles_links": [], - "username": "ZSymZdG4dJkw" - } - } -} \ No newline at end of file From d3675f54d5bcdfab35a9123817ffdbd8af6befae Mon Sep 17 00:00:00 2001 From: "fspezi@xilium.it" Date: Thu, 9 Mar 2017 17:29:34 +0100 Subject: [PATCH 05/10] Cleaning code --- src/corelib/Flurl/PreparedRequest.cs | 46 ---------------------------- src/corelib/OpenStack.csproj | 5 --- 2 files changed, 51 deletions(-) diff --git a/src/corelib/Flurl/PreparedRequest.cs b/src/corelib/Flurl/PreparedRequest.cs index ed5a980ed..666f290fa 100644 --- a/src/corelib/Flurl/PreparedRequest.cs +++ b/src/corelib/Flurl/PreparedRequest.cs @@ -142,37 +142,6 @@ public PreparedRequest SettingTimeout(TimeSpan defaultTimeout) CancellationToken = cancellationToken; return this; } - - /// - /// Prepares the client to send a POST request containing form - /// - public PreparedRequest PreparePostForm(IEnumerable>> formData, CancellationToken cancellationToken = default(CancellationToken)) - { - Verb = HttpMethod.Post; - - // var multipartFormDataContent = new MultipartFormDataContent(); - //multipartFormDataContent.Add(new StringContent(pairValue), pair.Key.ToLowerInvariant().Replace('-', '_')); - /*foreach (var pair in formData) - { - foreach (var pairValue in pair.Value) - { - multipartFormDataContent.Add(new StringContent(pairValue), pair.Key.ToLowerInvariant().Replace('-', '_')); - } - } - Content = multipartFormDataContent;*/ - // this.PostUrlEncodedAsync(formData, cancellationToken); - Content = new CapturedUrlEncodedContent( - Settings.UrlEncodedSerializer.Serialize( - formData.SelectMany( - pair => pair.Value, - (pair, pairVal) => new KeyValuePair(pair.Key, pairVal) - ) - ) - ); - - CancellationToken = cancellationToken; - return this; - } /// /// Prepares the client to send a POST request containing data in content Header @@ -198,21 +167,6 @@ public PreparedRequest SettingTimeout(TimeSpan defaultTimeout) return this; } - /// - /// Prepares the client to send a POST request containing data in header - /// - public PreparedRequest PreparePostHeader(IEnumerable data, CancellationToken cancellationToken = default(CancellationToken)) - { - Verb = HttpMethod.Post; - foreach (var keyValuePair in data) - { - this.HttpClient.DefaultRequestHeaders.Add(keyValuePair.MetadataKey, keyValuePair.MetadataValue); - } - CancellationToken = cancellationToken; - return this; - } - - /// /// Prepares the client to send a PUT request containing json diff --git a/src/corelib/OpenStack.csproj b/src/corelib/OpenStack.csproj index 134c4f450..d3b115fe6 100644 --- a/src/corelib/OpenStack.csproj +++ b/src/corelib/OpenStack.csproj @@ -891,11 +891,6 @@ - - - - - From b7eb2c58449bbff9c1dd4924c48f0d4234873e3c Mon Sep 17 00:00:00 2001 From: Flavio Spezi Date: Sat, 11 Mar 2017 13:39:58 +0100 Subject: [PATCH 06/10] Code cleaning --- src/corelib/Authentication/AuthenticatedMessageHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/Authentication/AuthenticatedMessageHandler.cs b/src/corelib/Authentication/AuthenticatedMessageHandler.cs index 178d6e092..34ed9a0f0 100644 --- a/src/corelib/Authentication/AuthenticatedMessageHandler.cs +++ b/src/corelib/Authentication/AuthenticatedMessageHandler.cs @@ -30,7 +30,7 @@ protected async override Task SendAsync(HttpRequestMessage try { var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); - var contentStr = await response.Content.ReadAsStringAsync(); + // var contentStr = await response.Content.ReadAsStringAsync(); return response; } catch (FlurlHttpException ex) From e03677b3c4bae208507504a61381084fde35391e Mon Sep 17 00:00:00 2001 From: Flavio Spezi Date: Sat, 11 Mar 2017 13:41:05 +0100 Subject: [PATCH 07/10] ObjectStorage: add ContentObjectFilters to filter ContentObjects in request object list. --- src/corelib/Flurl/PreparedRequest.cs | 15 +++- .../ObjectStorage/v1/BulkDeleteResult.cs | 69 +++++++++++++++++++ .../ObjectStorage/v1/ContainerObject.cs | 4 +- .../ContentObjectFilterBase.cs | 18 +++++ .../ContentObjectFilterCollection.cs | 48 +++++++++++++ .../IContentObjectFilter.cs | 22 ++++++ .../PathContentObjectFilter.cs | 27 ++++++++ .../SearchPrefixContentObjectFilter.cs | 28 ++++++++ .../SearchRangeContentObjectFilter.cs | 43 ++++++++++++ .../TakeContentObjectFilter.cs | 27 ++++++++ .../v1/ObjectStorageApiBuilder.cs | 61 +++++++++++++++- .../ObjectStorage/v1/ObjectStorageService.cs | 21 +++++- .../Serialization/HttpStatusCodeConverter.cs | 58 ++++++++++++++++ src/corelib/OpenStack.csproj | 9 +++ 14 files changed, 443 insertions(+), 7 deletions(-) create mode 100644 src/corelib/ObjectStorage/v1/BulkDeleteResult.cs create mode 100644 src/corelib/ObjectStorage/v1/ContentObjectFilters/ContentObjectFilterBase.cs create mode 100644 src/corelib/ObjectStorage/v1/ContentObjectFilters/ContentObjectFilterCollection.cs create mode 100644 src/corelib/ObjectStorage/v1/ContentObjectFilters/IContentObjectFilter.cs create mode 100644 src/corelib/ObjectStorage/v1/ContentObjectFilters/PathContentObjectFilter.cs create mode 100644 src/corelib/ObjectStorage/v1/ContentObjectFilters/SearchPrefixContentObjectFilter.cs create mode 100644 src/corelib/ObjectStorage/v1/ContentObjectFilters/SearchRangeContentObjectFilter.cs create mode 100644 src/corelib/ObjectStorage/v1/ContentObjectFilters/TakeContentObjectFilter.cs create mode 100644 src/corelib/ObjectStorage/v1/Serialization/HttpStatusCodeConverter.cs diff --git a/src/corelib/Flurl/PreparedRequest.cs b/src/corelib/Flurl/PreparedRequest.cs index 666f290fa..cf79c7be9 100644 --- a/src/corelib/Flurl/PreparedRequest.cs +++ b/src/corelib/Flurl/PreparedRequest.cs @@ -166,7 +166,20 @@ public PreparedRequest SettingTimeout(TimeSpan defaultTimeout) CancellationToken = cancellationToken; return this; } - + + /// + /// Prepares the client to send a POST request containing data in content Header + /// + /// + /// + /// + public PreparedRequest PreparePostContentTestPlain(string data, CancellationToken cancellationToken = default(CancellationToken)) + { + Verb = HttpMethod.Post; + Content = new System.Net.Http.StringContent(data); + CancellationToken = cancellationToken; + return this; + } /// /// Prepares the client to send a PUT request containing json diff --git a/src/corelib/ObjectStorage/v1/BulkDeleteResult.cs b/src/corelib/ObjectStorage/v1/BulkDeleteResult.cs new file mode 100644 index 000000000..720494e1d --- /dev/null +++ b/src/corelib/ObjectStorage/v1/BulkDeleteResult.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using OpenStack.ObjectStorage.v1.Serialization; + +namespace OpenStack.ObjectStorage.v1 { + + /// + /// Result of BulkDelete operation. + /// See . + /// + public class BulkDeleteResult { + + /// + /// Items found + /// + [JsonProperty("Number Not Found")] + public int? NumberNotFound { get; set; } + + /// + /// Items deleted + /// + [JsonProperty("Number Deleted")] + public int? NumberDeleted { get; set; } + + /// + /// Response status + /// + [JsonProperty("Response Status")] + [JsonConverter(typeof(HttpStatusCodeConverter))] + public System.Net.HttpStatusCode ResponseStatus { get; set; } + + /// + /// Response body + /// + [JsonProperty("Response Body")] + public string ResponseBody { get; set; } + + /// + /// Errors + /// + [JsonProperty("Errors")] + public ErrorItem[] Errors { get; set; } + + + /// + /// Error item + /// + public class ErrorItem + { + + /// + /// Item fullname + /// + [JsonProperty("Name")] + public string Name { get; set; } + + /// + /// Error status + /// + [JsonProperty("Status")] + public string Status { get; set; } + } + + } +} diff --git a/src/corelib/ObjectStorage/v1/ContainerObject.cs b/src/corelib/ObjectStorage/v1/ContainerObject.cs index c85776514..f92ffd80d 100644 --- a/src/corelib/ObjectStorage/v1/ContainerObject.cs +++ b/src/corelib/ObjectStorage/v1/ContainerObject.cs @@ -31,10 +31,10 @@ public class ContainerObject { public long Bytes { get; set; } /// - /// Object name + /// Object fullname /// [JsonProperty("name")] - public string Name { get; set; } + public string FullName { get; set; } /// /// Object content type diff --git a/src/corelib/ObjectStorage/v1/ContentObjectFilters/ContentObjectFilterBase.cs b/src/corelib/ObjectStorage/v1/ContentObjectFilters/ContentObjectFilterBase.cs new file mode 100644 index 000000000..70c5bd8ad --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContentObjectFilters/ContentObjectFilterBase.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.ContentObjectFilters { + + /// + /// Base class for ContentObjectFilter classes + /// + public abstract class ContentObjectFilterBase : IContentObjectFilter { + + /// + public abstract IEnumerable> CompileQueryParams(); + + } +} diff --git a/src/corelib/ObjectStorage/v1/ContentObjectFilters/ContentObjectFilterCollection.cs b/src/corelib/ObjectStorage/v1/ContentObjectFilters/ContentObjectFilterCollection.cs new file mode 100644 index 000000000..152512bf5 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContentObjectFilters/ContentObjectFilterCollection.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.ContentObjectFilters { + + /// + /// Collection of ContentObjectFilter + /// + public class ContentObjectFilterCollection : List { + + /// + /// Create new instance of ContentObjectFilter collection with preloaded items. + /// + public ContentObjectFilterCollection() + { + + } + + /// + /// Create new instance of ContentObjectFilter collection with preloaded items. + /// + /// + public ContentObjectFilterCollection(IEnumerable collection) : base(collection) + { + + } + + + /// + /// Create QueryParams to append to URL + /// + /// + public IEnumerable> CompileQueryParams() + { + foreach (var filter in this) + { + foreach (var compileQueryParam in filter.CompileQueryParams()) + { + yield return compileQueryParam; + } + } + } + + } +} diff --git a/src/corelib/ObjectStorage/v1/ContentObjectFilters/IContentObjectFilter.cs b/src/corelib/ObjectStorage/v1/ContentObjectFilters/IContentObjectFilter.cs new file mode 100644 index 000000000..072578115 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContentObjectFilters/IContentObjectFilter.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.ContentObjectFilters { + + /// + /// Define a filter for Content Object search + /// + public interface IContentObjectFilter + { + + /// + /// Create query parameters to append to URL + /// + /// + IEnumerable> CompileQueryParams(); + + } +} diff --git a/src/corelib/ObjectStorage/v1/ContentObjectFilters/PathContentObjectFilter.cs b/src/corelib/ObjectStorage/v1/ContentObjectFilters/PathContentObjectFilter.cs new file mode 100644 index 000000000..eaf7fce27 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContentObjectFilters/PathContentObjectFilter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.ContentObjectFilters { + + /// + /// Define a filter to obtain items in specified container path. + /// + public class PathContentObjectFilter : ContentObjectFilterBase { + + /// + /// The path of container. + /// + public string Path { get; set; } + + + /// + public override IEnumerable> CompileQueryParams() + { + yield return new KeyValuePair("path", this.Path); + } + } +} diff --git a/src/corelib/ObjectStorage/v1/ContentObjectFilters/SearchPrefixContentObjectFilter.cs b/src/corelib/ObjectStorage/v1/ContentObjectFilters/SearchPrefixContentObjectFilter.cs new file mode 100644 index 000000000..e5ba2bdeb --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContentObjectFilters/SearchPrefixContentObjectFilter.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.ContentObjectFilters { + + /// + /// Define a filter to obtain items where fullname starts with prefix. + /// Alert: do not work when combined with . + /// + public class SearchPrefixContentObjectFilter : ContentObjectFilterBase { + + /// + /// The prefix of fullname items that will be return. + /// + public string PathPrefix { get; set; } + + + /// + public override IEnumerable> CompileQueryParams() + { + yield return new KeyValuePair("prefix", this.PathPrefix); + } + } +} diff --git a/src/corelib/ObjectStorage/v1/ContentObjectFilters/SearchRangeContentObjectFilter.cs b/src/corelib/ObjectStorage/v1/ContentObjectFilters/SearchRangeContentObjectFilter.cs new file mode 100644 index 000000000..792cd8fc9 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContentObjectFilters/SearchRangeContentObjectFilter.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.ContentObjectFilters { + + /// + /// Define a filter to obtain items that are inside a specified range. + /// Items returned will be between and . + /// + public class SearchRangeContentObjectFilter : ContentObjectFilterBase { + + /// + /// The fullpath item to start search. If Null is considered as "not defined". + /// Note: selected item are fullpath greater than . + /// + public string StartMarker { get; set; } + + /// + /// The fullpath item to end search. If Null is considered as "not defined". + /// Note: selected item are fullpath lower than . + /// + public string EndMarker { get; set; } + + + /// + public override IEnumerable> CompileQueryParams() + { + if (this.StartMarker != null) + { + yield return new KeyValuePair("marker", this.StartMarker); + } + + if (this.EndMarker != null) + { + yield return new KeyValuePair("end_marker", this.EndMarker); + } + } + } +} diff --git a/src/corelib/ObjectStorage/v1/ContentObjectFilters/TakeContentObjectFilter.cs b/src/corelib/ObjectStorage/v1/ContentObjectFilters/TakeContentObjectFilter.cs new file mode 100644 index 000000000..3790f3b32 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContentObjectFilters/TakeContentObjectFilter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.ContentObjectFilters { + + /// + /// Define a filter to obtain first # items. + /// + public class TakeContentObjectFilter : ContentObjectFilterBase { + + /// + /// The limit of items to return. + /// + public int TakeLimit { get; set; } + + + /// + public override IEnumerable> CompileQueryParams() + { + yield return new KeyValuePair("limit", this.TakeLimit.ToString("0", CultureInfo.InvariantCulture)); + } + } +} diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs b/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs index 24a085238..97f90ed37 100644 --- a/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs +++ b/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Flurl; @@ -8,6 +10,7 @@ using Flurl.Http; using OpenStack.Authentication; using OpenStack.Networking.v2.Serialization; +using OpenStack.ObjectStorage.v1.ContentObjectFilters; using OpenStack.ObjectStorage.v1.Serialization; using OpenStack.Serialization; @@ -46,6 +49,7 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider Endpoint = new ServiceEndpoint(serviceType, authenticationProvider, region, useInternalUrl); } + #region Tenants /* /// @@ -65,8 +69,10 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider } */ #endregion - + + #region Containers + /// /// Lists all containers associated with the current tenant. /// @@ -77,7 +83,7 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider public async Task ListContainersAsync(CancellationToken cancellationToken = default(CancellationToken)) { Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); - + return endpoint .SetQueryParam("format", "json") .Authenticate(AuthenticationProvider) @@ -102,6 +108,31 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider .Authenticate(AuthenticationProvider) .PrepareGet(cancellationToken); } + + /// + /// Gets the container content of specified container. + /// + /// The container identifier. + /// Filters to select a range of objects. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The content of container. + /// + public virtual async Task GetContainerContentAsync(string containerName, ContentObjectFilterCollection filterCollection, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + if (filterCollection != null) + { + endpoint = endpoint.SetQueryParams(filterCollection.CompileQueryParams()); + } + + return endpoint + .SetQueryParam("format", "json") + .AppendPathSegments(containerName) + .Authenticate(AuthenticationProvider) + .PrepareGet(cancellationToken); + } /// /// Gets the container content of specified container. @@ -222,7 +253,7 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider /// Delete the object in specified container. /// /// The container identifier. - /// The path of object. + /// The path object. /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// /// Nothing @@ -237,6 +268,30 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider .PrepareDelete(cancellationToken); } + /// + /// Delete the object in specified container. + /// + /// The container identifier. + /// The list path object. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Nothing + /// + public virtual async Task DeleteContainerObjectListAsync(string containerName, IEnumerable objectPathList, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + var request = endpoint + .AppendPathSegments(containerName) + .SetQueryParam("bulk-delete", "true") + .Authenticate(AuthenticationProvider) + .PreparePostContentTestPlain(string.Join("\n", objectPathList.Select(item => containerName + "/" + item)), cancellationToken); + + request.HttpClient.DefaultRequestHeaders.Add("Accept", "application/json"); + + return request; + } + /// /// Get the metadata of object in specified container. /// diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs index b9424c261..d81a98b7f 100644 --- a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs +++ b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs @@ -10,6 +10,7 @@ using Flurl.Http; using OpenStack.Authentication; using OpenStack.Networking.v2.Serialization; +using OpenStack.ObjectStorage.v1.ContentObjectFilters; using OpenStack.ObjectStorage.v1.Metadata.ContainerMetadata; using OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata; using OpenStack.ObjectStorage.v1.Serialization; @@ -58,6 +59,15 @@ public ObjectStorageService(IAuthenticationProvider authenticationProvider, stri .ReceiveJson(); } + /// + public async Task GetContainerContentAsync(string containerId, ContentObjectFilterCollection filterCollection, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .GetContainerContentAsync(containerId, filterCollection, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + /// public async Task CreateContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) { @@ -137,6 +147,15 @@ await _objectStorageApiBuilder .SendAsync(); } + /// + public async Task DeleteContainerObjectListAsync(string containerId, IEnumerable objectPathList, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .DeleteContainerObjectListAsync(containerId, objectPathList, cancellationToken) + .SendAsync() + .ReceiveJson(); + } + /// public async Task ReadContainerObjectMetadataAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) { @@ -165,7 +184,7 @@ await _objectStorageApiBuilder var statusCode = request.StatusCode; - return statusCode == HttpStatusCode.OK; + return (int)statusCode < 300; } /// diff --git a/src/corelib/ObjectStorage/v1/Serialization/HttpStatusCodeConverter.cs b/src/corelib/ObjectStorage/v1/Serialization/HttpStatusCodeConverter.cs new file mode 100644 index 000000000..37ed954e7 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Serialization/HttpStatusCodeConverter.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using OpenStack.Networking.v2; + +namespace OpenStack.ObjectStorage.v1.Serialization +{ + /// + /// Handles serialization/deserialization for . + /// + public class HttpStatusCodeConverter : JsonConverter + { + /// + /// Writes the json. + /// + /// The writer. + /// The value. + /// The serializer. + /// + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + return; + + var httpStatus = (System.Net.HttpStatusCode)value; + + writer.WriteValue(string.Format("{0} {1}", (int)httpStatus, httpStatus.ToString())); + } + + /// + /// Reads the json. + /// + /// The reader. + /// Type of the object. + /// The existing value. + /// The serializer. + /// + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.String) throw new JsonSerializationException("Unexpected end when reading HttpStatusCode."); + + var strValue = reader.Value.ToString(); + + return (System.Net.HttpStatusCode) int.Parse(strValue.Substring(0, strValue.IndexOf(" "))); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + public override bool CanConvert(Type objectType) + { + return typeof (System.Net.HttpStatusCode).IsAssignableFrom(objectType); + } + } +} \ No newline at end of file diff --git a/src/corelib/OpenStack.csproj b/src/corelib/OpenStack.csproj index d3b115fe6..a52ff4df7 100644 --- a/src/corelib/OpenStack.csproj +++ b/src/corelib/OpenStack.csproj @@ -232,6 +232,13 @@ + + + + + + + @@ -246,10 +253,12 @@ + + From c5efbdefbfdc39cb960a9727996795c90e50a243 Mon Sep 17 00:00:00 2001 From: Flavio Spezi Date: Sat, 11 Mar 2017 15:08:21 +0100 Subject: [PATCH 08/10] ObjectStorage: add Delimiter filter. --- .../ObjectStorage/v1/ContainerDirectory.cs | 24 +++ .../ObjectStorage/v1/ContainerItemType.cs | 25 +++ .../ObjectStorage/v1/ContainerObject.cs | 5 +- .../DelimiterContentObjectFilter.cs | 36 +++++ .../ObjectStorage/v1/IContainerItem.cs | 28 ++++ .../ObjectStorage/v1/ObjectStorageService.cs | 12 +- ...llection.cs => ContainerItemCollection.cs} | 10 +- .../Serialization/ContainerItemConverter.cs | 148 ++++++++++++++++++ src/corelib/OpenStack.csproj | 7 +- 9 files changed, 282 insertions(+), 13 deletions(-) create mode 100644 src/corelib/ObjectStorage/v1/ContainerDirectory.cs create mode 100644 src/corelib/ObjectStorage/v1/ContainerItemType.cs create mode 100644 src/corelib/ObjectStorage/v1/ContentObjectFilters/DelimiterContentObjectFilter.cs create mode 100644 src/corelib/ObjectStorage/v1/IContainerItem.cs rename src/corelib/ObjectStorage/v1/Serialization/{ContainerObjectCollection.cs => ContainerItemCollection.cs} (73%) create mode 100644 src/corelib/ObjectStorage/v1/Serialization/ContainerItemConverter.cs diff --git a/src/corelib/ObjectStorage/v1/ContainerDirectory.cs b/src/corelib/ObjectStorage/v1/ContainerDirectory.cs new file mode 100644 index 000000000..f3af1e3c4 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContainerDirectory.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace OpenStack.ObjectStorage.v1 { + + /// + /// Represent an Object of container + /// + public class ContainerDirectory : IContainerItem { + + /// + /// Directory fullname + /// + [JsonProperty("subdir")] + public string FullName { get; set; } + + /// + public ContainerItemType ItemType { get { return ContainerItemType.Directory; } } + } +} diff --git a/src/corelib/ObjectStorage/v1/ContainerItemType.cs b/src/corelib/ObjectStorage/v1/ContainerItemType.cs new file mode 100644 index 000000000..1961d9017 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContainerItemType.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1 { + + /// + /// List of ContainerItem types + /// + public enum ContainerItemType { + + /// + /// ContainerObject, like a file + /// + Object = 1, + + /// + /// ContainerDirectoty, like a folder + /// + Directory = 2 + + } +} diff --git a/src/corelib/ObjectStorage/v1/ContainerObject.cs b/src/corelib/ObjectStorage/v1/ContainerObject.cs index f92ffd80d..78bd30ae2 100644 --- a/src/corelib/ObjectStorage/v1/ContainerObject.cs +++ b/src/corelib/ObjectStorage/v1/ContainerObject.cs @@ -10,7 +10,7 @@ namespace OpenStack.ObjectStorage.v1 { /// /// Represent an Object of container /// - public class ContainerObject { + public class ContainerObject : IContainerItem { /// /// Hash of Object @@ -41,6 +41,9 @@ public class ContainerObject { /// [JsonProperty("content_type")] public string ContentType { get; set; } + + /// + public ContainerItemType ItemType { get { return ContainerItemType.Object; } } } } diff --git a/src/corelib/ObjectStorage/v1/ContentObjectFilters/DelimiterContentObjectFilter.cs b/src/corelib/ObjectStorage/v1/ContentObjectFilters/DelimiterContentObjectFilter.cs new file mode 100644 index 000000000..f30ecf43f --- /dev/null +++ b/src/corelib/ObjectStorage/v1/ContentObjectFilters/DelimiterContentObjectFilter.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenStack.ObjectStorage.v1.ContentObjectFilters { + + /// + /// Define a filter that lock search inside folder when find string in item fullname. + /// When combined with it returns Subfolders. + /// + public class DelimiterContentObjectFilter : ContentObjectFilterBase { + + /// + /// Create new instance of filter and preload properties. + /// + public DelimiterContentObjectFilter() + { + this.Delimiter = "/"; + } + + /// + /// The delimiter string to find in item fullname. + /// + public string Delimiter { get; set; } + + + /// + public override IEnumerable> CompileQueryParams() + { + yield return new KeyValuePair("delimiter", this.Delimiter); + } + } +} diff --git a/src/corelib/ObjectStorage/v1/IContainerItem.cs b/src/corelib/ObjectStorage/v1/IContainerItem.cs new file mode 100644 index 000000000..d84e3d17b --- /dev/null +++ b/src/corelib/ObjectStorage/v1/IContainerItem.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using OpenStack.ObjectStorage.v1.Serialization; + +namespace OpenStack.ObjectStorage.v1 { + + /// + /// Represents a item of container. + /// + [JsonConverter(typeof(ContainerItemConverter))] + public interface IContainerItem { + + /// + /// Fullname of item + /// + string FullName { get; set; } + + /// + /// Indicates the type of item + /// + ContainerItemType ItemType { get; } + + } +} diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs index d81a98b7f..1fd66c285 100644 --- a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs +++ b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs @@ -51,21 +51,21 @@ public ObjectStorageService(IAuthenticationProvider authenticationProvider, stri } /// - public async Task GetContainerContentAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + public async Task GetContainerContentAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder .GetContainerContentAsync(containerId, cancellationToken) .SendAsync() - .ReceiveJson(); + .ReceiveJson(); } /// - public async Task GetContainerContentAsync(string containerId, ContentObjectFilterCollection filterCollection, CancellationToken cancellationToken = default(CancellationToken)) + public async Task GetContainerContentAsync(string containerId, ContentObjectFilterCollection filterCollection, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder .GetContainerContentAsync(containerId, filterCollection, cancellationToken) .SendAsync() - .ReceiveJson(); + .ReceiveJson(); } /// @@ -114,12 +114,12 @@ await _objectStorageApiBuilder #region Container Objects /// - public async Task> GetContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + public async Task> GetContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder .GetContainerObjectAsync(containerId, objectPath, cancellationToken) .SendAsync() - .ReceiveJson(); + .ReceiveJson(); } /// diff --git a/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs b/src/corelib/ObjectStorage/v1/Serialization/ContainerItemCollection.cs similarity index 73% rename from src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs rename to src/corelib/ObjectStorage/v1/Serialization/ContainerItemCollection.cs index 143482aa1..4b4ed1d88 100644 --- a/src/corelib/ObjectStorage/v1/Serialization/ContainerObjectCollection.cs +++ b/src/corelib/ObjectStorage/v1/Serialization/ContainerItemCollection.cs @@ -9,20 +9,20 @@ namespace OpenStack.ObjectStorage.v1.Serialization /// /// /// - public class ContainerObjectCollection : List + public class ContainerItemCollection : List { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public ContainerObjectCollection() + public ContainerItemCollection() { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The container objects. - public ContainerObjectCollection(IEnumerable containerObjects) : base(containerObjects) + public ContainerItemCollection(IEnumerable containerObjects) : base(containerObjects) { } } diff --git a/src/corelib/ObjectStorage/v1/Serialization/ContainerItemConverter.cs b/src/corelib/ObjectStorage/v1/Serialization/ContainerItemConverter.cs new file mode 100644 index 000000000..c73c79f0e --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Serialization/ContainerItemConverter.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Newtonsoft.Json; +using OpenStack.Networking.v2; + +namespace OpenStack.ObjectStorage.v1.Serialization +{ + /// + /// Handles serialization/deserialization for . + /// + /// + /// + public class ContainerItemConverter : JsonConverter + { + /// + /// Writes the json. + /// + /// The writer. + /// The value. + /// The serializer. + /// + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + return; + + var containerItem = (IContainerItem) value; + switch (containerItem.ItemType) + { + case ContainerItemType.Directory: + writer.WriteValue((ContainerDirectory)value); + break; + case ContainerItemType.Object: + writer.WriteValue((ContainerObject)value); + break; + default: + throw new InvalidEnumArgumentException(); + } + } + + /// + /// Reads the json. + /// + /// The reader. + /// Type of the object. + /// The existing value. + /// The serializer. + /// + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.StartObject) + ReadAndAssert(reader); + + if (reader.TokenType != JsonToken.PropertyName) + throw new JsonSerializationException("Unexpected end when reading IContainerItem."); + + var firstPropertyName = reader.Value.ToString(); + switch (firstPropertyName) + { + case "subdir": + // Is a directory + return readDirectory(reader, serializer); + case "bytes": + case "content_type": + case "hash": + case "last_modified": + case "name": + // Is an object + return readObject(reader, serializer); + default: + throw new JsonSerializationException("Unexpected end when reading IContainerItem."); + } + } + + private static ContainerDirectory readDirectory(JsonReader reader, JsonSerializer serializer) + { + var dirItem = new ContainerDirectory(); + + while (reader.TokenType == JsonToken.PropertyName) + { + var propertyName = reader.Value.ToString(); + ReadAndAssert(reader); + switch (propertyName) + { + case "subdir": + dirItem.FullName = serializer.Deserialize(reader); + break; + } + + ReadAndAssert(reader); + } + + return dirItem; + } + + private static ContainerObject readObject(JsonReader reader, JsonSerializer serializer) + { + var objItem = new ContainerObject(); + + while (reader.TokenType == JsonToken.PropertyName) + { + var propertyName = reader.Value.ToString(); + ReadAndAssert(reader); + switch (propertyName) + { + case "bytes": + objItem.Bytes = serializer.Deserialize(reader); + break; + case "content_type": + objItem.ContentType = serializer.Deserialize(reader); + break; + case "hash": + objItem.Hash = serializer.Deserialize(reader); + break; + case "last_modified": + var date = serializer.Deserialize(reader); + objItem.LastModified = new DateTime(date.Ticks, DateTimeKind.Utc); + break; + case "name": + objItem.FullName = serializer.Deserialize(reader); + break; + } + + ReadAndAssert(reader); + } + + return objItem; + } + + private static void ReadAndAssert(JsonReader reader) + { + if (!reader.Read()) + throw new JsonSerializationException("Unexpected end when reading IContainerItem."); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + public override bool CanConvert(Type objectType) + { + return typeof (IContainerItem).IsAssignableFrom(objectType); + } + } +} \ No newline at end of file diff --git a/src/corelib/OpenStack.csproj b/src/corelib/OpenStack.csproj index a52ff4df7..edb019ccd 100644 --- a/src/corelib/OpenStack.csproj +++ b/src/corelib/OpenStack.csproj @@ -231,6 +231,8 @@ + + @@ -238,7 +240,9 @@ + + @@ -257,7 +261,8 @@ - + + From f389125f334d8ae2ed588a8105983b0e8d84a8ba Mon Sep 17 00:00:00 2001 From: Flavio Spezi Date: Mon, 27 Mar 2017 11:25:01 +0200 Subject: [PATCH 09/10] Some bug fixes --- build/build.proj | 4 +- .../LastModifiedContainerObjectMetadata.cs | 6 +- .../TimestampContainerObjectMetadata.cs | 6 +- .../v1/Metadata/MetadataConverter.cs | 67 +++++++++++++++++-- .../v1/ObjectStorageApiBuilder.cs | 21 +++++- .../ObjectStorage/v1/ObjectStorageService.cs | 65 ++++++++++-------- 6 files changed, 124 insertions(+), 45 deletions(-) diff --git a/build/build.proj b/build/build.proj index e721f20dc..71a774586 100644 --- a/build/build.proj +++ b/build/build.proj @@ -44,8 +44,8 @@ - - + + diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/LastModifiedContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/LastModifiedContainerObjectMetadata.cs index 4fa17a90a..dcf5bfacc 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/LastModifiedContainerObjectMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/LastModifiedContainerObjectMetadata.cs @@ -25,10 +25,10 @@ public LastModifiedContainerObjectMetadata() : base("Last-Modified", false) /// /// Get or set LastModified of Object /// - public DateTime LastModified + public DateTimeOffset LastModified { - get { return MetadataConverter.ParseDateTimeSingleValue(this.MetadataValue); } - set { this.MetadataValue = MetadataConverter.SerializeDateTimeValue(value); } + get { return MetadataConverter.ParseDateTimeOffsetSingleValue(this.MetadataValue); } + set { this.MetadataValue = MetadataConverter.SerializeDateTimeOffsetValue(value); } } } diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs index 59f518d32..539eefe1d 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/TimestampContainerObjectMetadata.cs @@ -12,9 +12,7 @@ namespace OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata { /// public class TimestampContainerObjectMetadata : MetadataBase, IContainerObjectMetadata { - - private static readonly DateTime zeroDayUnixTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - + /// /// Create new instance /// @@ -35,7 +33,7 @@ public double Timestamp /// /// Get or set Timestamp of Object in DateTime format /// - public DateTime TimestampDate + public DateTimeOffset TimestampDate { get { return MetadataConverter.ParseTimestampSingleValue(this.MetadataValue); } set { this.MetadataValue = MetadataConverter.SerializeTimestampValue(value); } diff --git a/src/corelib/ObjectStorage/v1/Metadata/MetadataConverter.cs b/src/corelib/ObjectStorage/v1/Metadata/MetadataConverter.cs index 790637c9e..381d6437e 100644 --- a/src/corelib/ObjectStorage/v1/Metadata/MetadataConverter.cs +++ b/src/corelib/ObjectStorage/v1/Metadata/MetadataConverter.cs @@ -219,15 +219,72 @@ public static DateTime[] ParseDateTimeMultiValue(IEnumerable serializedV #endregion + #region DateTimeOffset converter + + /// + /// Serialize value to Metadata + /// + /// + public static string[] SerializeDateTimeOffsetValue(DateTimeOffset value) + { + return new [] { value.ToString(CultureInfo.InvariantCulture.DateTimeFormat) }; + } + + /// + /// Serialize value to Metadata + /// + /// + public static string[] SerializeDateTimeOffsetValue(IEnumerable value) + { + if (value == null) return null; + + return value.Select(item => item.ToString(CultureInfo.InvariantCulture.DateTimeFormat)).ToArray(); + } + + /// + /// Parse value from Metadata + /// + /// + public static DateTimeOffset ParseDateTimeOffsetSingleValue(IEnumerable serializedValue) + { + if (serializedValue == null) return DateTimeOffset.MinValue; + + var firstValue = serializedValue.FirstOrDefault(); + if (firstValue == null) return DateTimeOffset.MinValue; + + return DateTimeOffset.Parse(firstValue, + System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat, + DateTimeStyles.AssumeUniversal + ); + } + + /// + /// Parse value from Metadata + /// + /// + public static DateTimeOffset[] ParseDateTimeOffsetMultiValue(IEnumerable serializedValue) + { + if (serializedValue == null) return null; + + return serializedValue + .Select(item => DateTimeOffset.Parse(item, + System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat, + DateTimeStyles.AssumeUniversal + )) + .ToArray(); + } + + #endregion + #region Timestamp converter - private static readonly DateTime zeroDayUnixTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + private static readonly DateTimeOffset zeroDayUnixTime = new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, new TimeSpan(0)); /// /// Serialize value to Metadata /// /// - public static string[] SerializeTimestampValue(DateTime value) + public static string[] SerializeTimestampValue(DateTimeOffset value) { return new [] { value.Subtract(zeroDayUnixTime).TotalSeconds.ToString("R", CultureInfo.InvariantCulture.DateTimeFormat) }; } @@ -236,7 +293,7 @@ public static string[] SerializeTimestampValue(DateTime value) /// Serialize value to Metadata /// /// - public static string[] SerializeTimestampValue(IEnumerable value) + public static string[] SerializeTimestampValue(IEnumerable value) { if (value == null) return null; @@ -247,7 +304,7 @@ public static string[] SerializeTimestampValue(IEnumerable value) /// Parse value from Metadata /// /// - public static DateTime ParseTimestampSingleValue(IEnumerable serializedValue) + public static DateTimeOffset ParseTimestampSingleValue(IEnumerable serializedValue) { var doubleValue = ParseDoubleSingleValue(serializedValue); @@ -258,7 +315,7 @@ public static DateTime ParseTimestampSingleValue(IEnumerable serializedV /// Parse value from Metadata /// /// - public static DateTime[] ParseTimestampMultiValue(IEnumerable serializedValue) + public static DateTimeOffset[] ParseTimestampMultiValue(IEnumerable serializedValue) { if (serializedValue == null) return null; diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs b/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs index 97f90ed37..0fcb65053 100644 --- a/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs +++ b/src/corelib/ObjectStorage/v1/ObjectStorageApiBuilder.cs @@ -80,7 +80,7 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider /// /// A collection of containers associated with current tenant. /// - public async Task ListContainersAsync(CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task ListContainersAsync(CancellationToken cancellationToken = default(CancellationToken)) { Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); @@ -90,6 +90,22 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider .PrepareGet(cancellationToken); } + /// + /// Returns container Url + /// + /// The container identifier. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// The URL of container A collection of containers associated with current tenant. + /// + public virtual async Task GetContainerUrlAsync(string containerName, CancellationToken cancellationToken = default(CancellationToken)) + { + Url endpoint = await Endpoint.GetEndpoint(cancellationToken).ConfigureAwait(false); + + return endpoint + .AppendPathSegments(containerName); + } + /// /// Gets the container content of specified container. /// @@ -265,7 +281,8 @@ public ObjectStorageApiBuilder(IServiceType serviceType, IAuthenticationProvider return endpoint .AppendPathSegments(containerName, objectPath) .Authenticate(AuthenticationProvider) - .PrepareDelete(cancellationToken); + .PrepareDelete(cancellationToken) + .AllowHttpStatus(HttpStatusCode.NotFound); } /// diff --git a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs index 1fd66c285..9e6536500 100644 --- a/src/corelib/ObjectStorage/v1/ObjectStorageService.cs +++ b/src/corelib/ObjectStorage/v1/ObjectStorageService.cs @@ -49,39 +49,46 @@ public ObjectStorageService(IAuthenticationProvider authenticationProvider, stri .SendAsync() .ReceiveJson(); } + + /// + public async Task GetContainerUrlAsync(string containerName, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _objectStorageApiBuilder + .GetContainerUrlAsync(containerName, cancellationToken); + } /// - public async Task GetContainerContentAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + public async Task GetContainerContentAsync(string containerName, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder - .GetContainerContentAsync(containerId, cancellationToken) + .GetContainerContentAsync(containerName, cancellationToken) .SendAsync() .ReceiveJson(); } /// - public async Task GetContainerContentAsync(string containerId, ContentObjectFilterCollection filterCollection, CancellationToken cancellationToken = default(CancellationToken)) + public async Task GetContainerContentAsync(string containerName, ContentObjectFilterCollection filterCollection, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder - .GetContainerContentAsync(containerId, filterCollection, cancellationToken) + .GetContainerContentAsync(containerName, filterCollection, cancellationToken) .SendAsync() .ReceiveJson(); } /// - public async Task CreateContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + public async Task CreateContainerAsync(string containerName, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder - .CreateContainerAsync(containerId, cancellationToken) + .CreateContainerAsync(containerName, cancellationToken) .SendAsync() .ReceiveJson(); } /// - public async Task ReadContainerMetadataAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + public async Task ReadContainerMetadataAsync(string containerName, CancellationToken cancellationToken = default(CancellationToken)) { var requestHeaders = await _objectStorageApiBuilder - .ReadContainerMetadataAsync(containerId, cancellationToken) + .ReadContainerMetadataAsync(containerName, cancellationToken) .SendAsync() .ReceiveHeaders(); @@ -91,18 +98,18 @@ public ObjectStorageService(IAuthenticationProvider authenticationProvider, stri } /// - public async Task SaveContainerMetadataAsync(string containerId, ContainerMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) + public async Task SaveContainerMetadataAsync(string containerName, ContainerMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) { await _objectStorageApiBuilder - .SaveContainerMetadataAsync(containerId, metadataCollection, cancellationToken) + .SaveContainerMetadataAsync(containerName, metadataCollection, cancellationToken) .SendAsync(); } /// - public async Task DeleteContainerAsync(string containerId, CancellationToken cancellationToken = default(CancellationToken)) + public async Task DeleteContainerAsync(string containerName, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder - .DeleteContainerAsync(containerId, cancellationToken) + .DeleteContainerAsync(containerName, cancellationToken) .SendAsync(); } @@ -114,55 +121,55 @@ await _objectStorageApiBuilder #region Container Objects /// - public async Task> GetContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + public async Task GetContainerObjectAsync(string containerName, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder - .GetContainerObjectAsync(containerId, objectPath, cancellationToken) + .GetContainerObjectAsync(containerName, objectPath, cancellationToken) .SendAsync() - .ReceiveJson(); + .ReceiveStream(); } /// - public async Task UpdateContainerObjectAsync(string containerId, string objectPath, System.IO.Stream dataStream, CancellationToken cancellationToken = default(CancellationToken)) + public async Task UpdateContainerObjectAsync(string containerName, string objectPath, System.IO.Stream dataStream, CancellationToken cancellationToken = default(CancellationToken)) { await _objectStorageApiBuilder - .UpdateContainerObjectAsync(containerId, objectPath, dataStream, cancellationToken) + .UpdateContainerObjectAsync(containerName, objectPath, dataStream, cancellationToken) .SendAsync(); } /// - public async Task UpdateContainerObjectAsync(string containerId, string objectPath, string filePath, CancellationToken cancellationToken = default(CancellationToken)) + public async Task UpdateContainerObjectAsync(string containerName, string objectPath, string filePath, CancellationToken cancellationToken = default(CancellationToken)) { using (var dataStream = new System.IO.FileStream(filePath, FileMode.Open, FileAccess.Read)) { - await this.UpdateContainerObjectAsync(containerId, objectPath, dataStream, cancellationToken); + await this.UpdateContainerObjectAsync(containerName, objectPath, dataStream, cancellationToken); } } /// - public async Task DeleteContainerObjectAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + public async Task DeleteContainerObjectAsync(string containerName, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) { await _objectStorageApiBuilder - .DeleteContainerObjectAsync(containerId, objectPath, cancellationToken) + .DeleteContainerObjectAsync(containerName, objectPath, cancellationToken) .SendAsync(); } /// - public async Task DeleteContainerObjectListAsync(string containerId, IEnumerable objectPathList, CancellationToken cancellationToken = default(CancellationToken)) + public async Task DeleteContainerObjectListAsync(string containerName, IEnumerable objectPathList, CancellationToken cancellationToken = default(CancellationToken)) { return await _objectStorageApiBuilder - .DeleteContainerObjectListAsync(containerId, objectPathList, cancellationToken) + .DeleteContainerObjectListAsync(containerName, objectPathList, cancellationToken) .SendAsync() .ReceiveJson(); } /// - public async Task ReadContainerObjectMetadataAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + public async Task ReadContainerObjectMetadataAsync(string containerName, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) { // Note: Object metadata are inside both `request.Content.Headers` and `request.Headers`. var request = await _objectStorageApiBuilder - .ReadContainerObjectMetadataAsync(containerId, objectPath, cancellationToken) + .ReadContainerObjectMetadataAsync(containerName, objectPath, cancellationToken) .SendAsync(); var requestHeaders = new List>>(); @@ -176,10 +183,10 @@ await _objectStorageApiBuilder } /// - public async Task CheckContainerObjectExistsAsync(string containerId, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) + public async Task CheckContainerObjectExistsAsync(string containerName, string objectPath, CancellationToken cancellationToken = default(CancellationToken)) { var request = await _objectStorageApiBuilder - .ReadContainerObjectMetadataAsync(containerId, objectPath, cancellationToken) + .ReadContainerObjectMetadataAsync(containerName, objectPath, cancellationToken) .SendAsync(); var statusCode = request.StatusCode; @@ -188,10 +195,10 @@ await _objectStorageApiBuilder } /// - public async Task SaveContainerObjectMetadataAsync(string containerId, string objectPath, ContainerObjectMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) + public async Task SaveContainerObjectMetadataAsync(string containerName, string objectPath, ContainerObjectMetadataCollection metadataCollection, CancellationToken cancellationToken = default(CancellationToken)) { await _objectStorageApiBuilder - .SaveContainerObjectMetadataAsync(containerId, objectPath, metadataCollection, cancellationToken) + .SaveContainerObjectMetadataAsync(containerName, objectPath, metadataCollection, cancellationToken) .SendAsync(); } From 11383da9e38096266d6b1676b6c731f346d17aa4 Mon Sep 17 00:00:00 2001 From: Flavio Spezi Date: Tue, 18 Apr 2017 17:53:09 +0200 Subject: [PATCH 10/10] ContainerObjectMetadata: add CacheControlContainerObjectMetadata and ExpiresContainerObjectMetadata. --- src/corelib/Flurl/PreparedRequest.cs | 22 ++- .../CacheControlContainerObjectMetadata.cs | 169 ++++++++++++++++++ .../ExpiresContainerObjectMetadata.cs | 35 ++++ .../v1/Metadata/MetadataConverter.cs | 2 +- .../v1/Metadata/MetadataEnumValueAttribute.cs | 49 +++++ .../v1/ObjectStorageApiBuilder.cs | 2 +- src/corelib/OpenStack.csproj | 3 + 7 files changed, 274 insertions(+), 8 deletions(-) create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/CacheControlContainerObjectMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/ExpiresContainerObjectMetadata.cs create mode 100644 src/corelib/ObjectStorage/v1/Metadata/MetadataEnumValueAttribute.cs diff --git a/src/corelib/Flurl/PreparedRequest.cs b/src/corelib/Flurl/PreparedRequest.cs index cf79c7be9..2575e8244 100644 --- a/src/corelib/Flurl/PreparedRequest.cs +++ b/src/corelib/Flurl/PreparedRequest.cs @@ -155,14 +155,24 @@ public PreparedRequest SettingTimeout(TimeSpan defaultTimeout) Content = new System.Net.Http.StringContent(""); Content.Headers.Remove("Content-Type"); + foreach (var pair in formData) - { - foreach (var pairValue in pair.Value) + { + System.Net.Http.Headers.HttpHeaders headerColl; + + switch (pair.Key.ToLowerInvariant()) { - Content.Headers.Add(pair.Key, pairValue); - } + case "cache-control": + headerColl = this.HttpClient.DefaultRequestHeaders; + break; + default: + headerColl = this.Content.Headers; + break; + } + + headerColl.Add(pair.Key, pair.Value); } - + CancellationToken = cancellationToken; return this; } @@ -173,7 +183,7 @@ public PreparedRequest SettingTimeout(TimeSpan defaultTimeout) /// /// /// - public PreparedRequest PreparePostContentTestPlain(string data, CancellationToken cancellationToken = default(CancellationToken)) + public PreparedRequest PreparePostContentTextPlain(string data, CancellationToken cancellationToken = default(CancellationToken)) { Verb = HttpMethod.Post; Content = new System.Net.Http.StringContent(data); diff --git a/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/CacheControlContainerObjectMetadata.cs b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/CacheControlContainerObjectMetadata.cs new file mode 100644 index 000000000..4132b2ca4 --- /dev/null +++ b/src/corelib/ObjectStorage/v1/Metadata/ContainerObjectMetadata/CacheControlContainerObjectMetadata.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Newtonsoft.Json.Serialization; + +namespace OpenStack.ObjectStorage.v1.Metadata.ContainerObjectMetadata { + + /// + /// The `Cache-Control` header metadata. + /// + public class CacheControlContainerObjectMetadata : MetadataBase, IContainerObjectMetadata + { + + /// + /// Create new instance + /// + public CacheControlContainerObjectMetadata() : base("Cache-Control", false) + { + } + + /// + /// Get or set the Cacheability vale + /// + public CacheabilityEnum Cacheability + { + get + { + var metaValue = new CacheControlValue(this.MetadataValue); + return metaValue.Cacheability; + } + set + { + var metaValue = new CacheControlValue(this.MetadataValue); + metaValue.Cacheability = value; + this.MetadataValue = metaValue.ToMetadata(); + } + } + + /// + /// Get or set the max age of cached item + /// + public TimeSpan MaxAge + { + get + { + var metaValue = new CacheControlValue(this.MetadataValue); + return metaValue.MaxAge; + } + set + { + var metaValue = new CacheControlValue(this.MetadataValue); + metaValue.MaxAge = value; + this.MetadataValue = metaValue.ToMetadata(); + } + } + + private static bool tryGetCacheability(string metadataPart, out CacheabilityEnum value) + { + var part = metadataPart.Trim(); + + var enumValues = Enum.GetValues(typeof(CacheabilityEnum)).Cast(); + foreach (var enumValue in enumValues) + { + var enumValueAttr = MetadataEnumValueAttribute.GetAttribute(enumValue); + if (enumValueAttr.MetadataValue.Equals(part, StringComparison.InvariantCultureIgnoreCase)) + { + value = enumValue; + return true; + } + } + + value = CacheabilityEnum.NoCache; + return false; + } + + private static bool tryGetMaxAge(string metadataPart, out TimeSpan value) + { + var re = new Regex(@"\s*max-age=(?