From 1833d260869ba361676fa0b99bfd1928d5a37dbd Mon Sep 17 00:00:00 2001 From: tbombach Date: Wed, 13 Apr 2016 15:39:59 -0700 Subject: [PATCH 01/16] Adding a new primary type for unix time and updating namers to handle this type --- .../ClientModel/KnownPrimaryType.cs | 3 +- .../CSharp/CSharp/CSharpCodeNamer.cs | 7 +- .../CSharp/CSharp/ClientModelExtensions.cs | 5 ++ .../TemplateModels/MethodTemplateModel.cs | 18 ++++- .../CSharp/Templates/ModelTemplate.cshtml | 8 ++ .../Java/Java/TypeModels/PrimaryTypeModel.cs | 4 + .../NodeJS/NodeJS/NodeJsCodeNamer.cs | 4 + .../Python/Python/PythonCodeNamer.cs | 4 + .../Generators/Ruby/Ruby/RubyCodeNamer.cs | 4 + .../Modelers/Swagger/Model/SwaggerObject.cs | 4 + .../Serialization/UnixTimeJsonConverter.cs | 76 +++++++++++++++++++ 11 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime/Serialization/UnixTimeJsonConverter.cs diff --git a/AutoRest/AutoRest.Core/ClientModel/KnownPrimaryType.cs b/AutoRest/AutoRest.Core/ClientModel/KnownPrimaryType.cs index 200dcfe018dfc..94dd5ffd7b726 100644 --- a/AutoRest/AutoRest.Core/ClientModel/KnownPrimaryType.cs +++ b/AutoRest/AutoRest.Core/ClientModel/KnownPrimaryType.cs @@ -25,6 +25,7 @@ public enum KnownPrimaryType Boolean, Credentials, Uuid, - Base64Url + Base64Url, + UnixTime } } diff --git a/AutoRest/Generators/CSharp/CSharp/CSharpCodeNamer.cs b/AutoRest/Generators/CSharp/CSharp/CSharpCodeNamer.cs index d9562cf4ca63c..3504b01cb57d2 100644 --- a/AutoRest/Generators/CSharp/CSharp/CSharpCodeNamer.cs +++ b/AutoRest/Generators/CSharp/CSharp/CSharpCodeNamer.cs @@ -297,6 +297,10 @@ protected virtual IType NormalizePrimaryType(PrimaryType primaryType) { primaryType.Name = "ServiceClientCredentials"; } + else if (primaryType.Type == KnownPrimaryType.UnixTime) + { + primaryType.Name = "DateTime"; + } else if (primaryType.Type == KnownPrimaryType.Uuid) { primaryType.Name = "Guid"; @@ -401,7 +405,8 @@ public override string EscapeDefaultValue(string defaultValue, IType type) primaryType.Type == KnownPrimaryType.DateTimeRfc1123 || primaryType.Type == KnownPrimaryType.TimeSpan || primaryType.Type == KnownPrimaryType.ByteArray || - primaryType.Type == KnownPrimaryType.Base64Url) + primaryType.Type == KnownPrimaryType.Base64Url || + primaryType.Type == KnownPrimaryType.UnixTime) { return "SafeJsonConvert.DeserializeObject<" + primaryType.Name.TrimEnd('?') + diff --git a/AutoRest/Generators/CSharp/CSharp/ClientModelExtensions.cs b/AutoRest/Generators/CSharp/CSharp/ClientModelExtensions.cs index ded2055e683be..ae4d9f11159de 100644 --- a/AutoRest/Generators/CSharp/CSharp/ClientModelExtensions.cs +++ b/AutoRest/Generators/CSharp/CSharp/ClientModelExtensions.cs @@ -225,6 +225,10 @@ public static string ToString(this IType type, string clientReference, string re { serializationSettings = "new Base64UrlJsonConverter()"; } + else if (primaryType.Type == KnownPrimaryType.UnixTime) + { + serializationSettings = "new UnixTimeJsonConverter()"; + } } return string.Format(CultureInfo.InvariantCulture, @@ -268,6 +272,7 @@ public static bool IsValueType(this IType type) || primaryType.Type == KnownPrimaryType.Long || primaryType.Type == KnownPrimaryType.TimeSpan || primaryType.Type == KnownPrimaryType.DateTimeRfc1123 + || primaryType.Type == KnownPrimaryType.UnixTime || primaryType.Type == KnownPrimaryType.Uuid)); } diff --git a/AutoRest/Generators/CSharp/CSharp/TemplateModels/MethodTemplateModel.cs b/AutoRest/Generators/CSharp/CSharp/TemplateModels/MethodTemplateModel.cs index cc44708181ecf..d4a380df2765c 100644 --- a/AutoRest/Generators/CSharp/CSharp/TemplateModels/MethodTemplateModel.cs +++ b/AutoRest/Generators/CSharp/CSharp/TemplateModels/MethodTemplateModel.cs @@ -351,6 +351,14 @@ public string GetSerializationSettingsReference(IType serializationType) { return "new Base64UrlJsonConverter()"; } + else if (serializationType.IsPrimaryType(KnownPrimaryType.UnixTime) || + (sequenceType != null && sequenceType.ElementType is PrimaryType + && ((PrimaryType)sequenceType.ElementType).Type == KnownPrimaryType.UnixTime) || + (dictionaryType != null && dictionaryType.ValueType is PrimaryType + && ((PrimaryType)dictionaryType.ValueType).Type == KnownPrimaryType.UnixTime)) + { + return "new UnixTimeJsonConverter()"; + } return ClientReference + ".SerializationSettings"; } @@ -371,7 +379,7 @@ public string GetDeserializationSettingsReference(IType deserializationType) { return "new DateJsonConverter()"; } - if (deserializationType.IsPrimaryType(KnownPrimaryType.Base64Url) || + else if (deserializationType.IsPrimaryType(KnownPrimaryType.Base64Url) || (sequenceType != null && sequenceType.ElementType is PrimaryType && ((PrimaryType)sequenceType.ElementType).Type == KnownPrimaryType.Base64Url) || (dictionaryType != null && dictionaryType.ValueType is PrimaryType @@ -379,6 +387,14 @@ public string GetDeserializationSettingsReference(IType deserializationType) { return "new Base64UrlJsonConverter()"; } + else if (deserializationType.IsPrimaryType(KnownPrimaryType.UnixTime) || + (sequenceType != null && sequenceType.ElementType is PrimaryType + && ((PrimaryType)sequenceType.ElementType).Type == KnownPrimaryType.UnixTime) || + (dictionaryType != null && dictionaryType.ValueType is PrimaryType + && ((PrimaryType)dictionaryType.ValueType).Type == KnownPrimaryType.UnixTime)) + { + return "new UnixTimeJsonConverter()"; + } return ClientReference + ".DeserializationSettings"; } diff --git a/AutoRest/Generators/CSharp/CSharp/Templates/ModelTemplate.cshtml b/AutoRest/Generators/CSharp/CSharp/Templates/ModelTemplate.cshtml index 2bcf9676f0951..942e67ef809e3 100644 --- a/AutoRest/Generators/CSharp/CSharp/Templates/ModelTemplate.cshtml +++ b/AutoRest/Generators/CSharp/CSharp/Templates/ModelTemplate.cshtml @@ -123,6 +123,10 @@ namespace @(Settings.Namespace).Models { @:[JsonConverter(typeof(Base64UrlJsonConverter))] } + if (property.Type.IsPrimaryType(KnownPrimaryType.UnixTime)) + { + @:[JsonConverter(typeof(UnixTimeJsonConverter))] + } @:[JsonProperty(PropertyName = "@property.SerializedName")] @:public @property.Type.Name @property.Name { get; @(property.IsReadOnly ? "private " : "")set; } @EmptyLine @@ -145,6 +149,10 @@ namespace @(Settings.Namespace).Models { @:[JsonConverter(typeof(Base64UrlJsonConverter))] } + if (property.Type.IsPrimaryType(KnownPrimaryType.UnixTime)) + { + @:[JsonConverter(typeof(UnixTimeJsonConverter))] + } @:[JsonProperty(PropertyName = "@property.SerializedName")] @:public static @property.Type.Name @property.Name { get; private set; } @EmptyLine diff --git a/AutoRest/Generators/Java/Java/TypeModels/PrimaryTypeModel.cs b/AutoRest/Generators/Java/Java/TypeModels/PrimaryTypeModel.cs index 2d5a2fad78626..d6990c79ff680 100644 --- a/AutoRest/Generators/Java/Java/TypeModels/PrimaryTypeModel.cs +++ b/AutoRest/Generators/Java/Java/TypeModels/PrimaryTypeModel.cs @@ -202,6 +202,10 @@ private void Initialize(PrimaryType primaryType) Name = "Period"; _imports.Add("org.joda.time.Period"); } + else if (primaryType.Type == KnownPrimaryType.UnixTime) + { + Name = "long"; + } else if (primaryType.Type == KnownPrimaryType.Uuid) { Name = "UUID"; diff --git a/AutoRest/Generators/NodeJS/NodeJS/NodeJsCodeNamer.cs b/AutoRest/Generators/NodeJS/NodeJS/NodeJsCodeNamer.cs index 53ac858681ef1..d2f43a402f589 100644 --- a/AutoRest/Generators/NodeJS/NodeJS/NodeJsCodeNamer.cs +++ b/AutoRest/Generators/NodeJS/NodeJS/NodeJsCodeNamer.cs @@ -393,6 +393,10 @@ private static IType NormalizePrimaryType(PrimaryType primaryType) { primaryType.Name = "moment.duration"; } + else if (primaryType.Type == KnownPrimaryType.UnixTime) + { + primaryType.Name = "Number"; + } else if (primaryType.Type == KnownPrimaryType.Uuid) { primaryType.Name = "Uuid"; diff --git a/AutoRest/Generators/Python/Python/PythonCodeNamer.cs b/AutoRest/Generators/Python/Python/PythonCodeNamer.cs index 4aebfb6378a11..11ae0801acce4 100644 --- a/AutoRest/Generators/Python/Python/PythonCodeNamer.cs +++ b/AutoRest/Generators/Python/Python/PythonCodeNamer.cs @@ -376,6 +376,10 @@ private static IType NormalizePrimaryType(PrimaryType primaryType) { primaryType.Name = "Decimal"; } + else if (primaryType.Type == KnownPrimaryType.UnixTime) + { + primaryType.Name = "long"; + } else if (primaryType.Type == KnownPrimaryType.Object) // Revisit here { primaryType.Name = "object"; diff --git a/AutoRest/Generators/Ruby/Ruby/RubyCodeNamer.cs b/AutoRest/Generators/Ruby/Ruby/RubyCodeNamer.cs index bcb90309c677e..c40423ee1b4ae 100644 --- a/AutoRest/Generators/Ruby/Ruby/RubyCodeNamer.cs +++ b/AutoRest/Generators/Ruby/Ruby/RubyCodeNamer.cs @@ -372,6 +372,10 @@ private IType NormalizePrimaryType(PrimaryType primaryType) { primaryType.Name = "Duration"; } + else if (primaryType.Type == KnownPrimaryType.UnixTime) + { + primaryType.Name = "Bignum"; + } else if (primaryType.Type == KnownPrimaryType.Object) { primaryType.Name = "Object"; diff --git a/AutoRest/Modelers/Swagger/Model/SwaggerObject.cs b/AutoRest/Modelers/Swagger/Model/SwaggerObject.cs index 2ca86ef10d3e1..02bfb0d23b8bc 100644 --- a/AutoRest/Modelers/Swagger/Model/SwaggerObject.cs +++ b/AutoRest/Modelers/Swagger/Model/SwaggerObject.cs @@ -132,6 +132,10 @@ public PrimaryType ToType() { return new PrimaryType(KnownPrimaryType.Long); } + if (string.Equals("unixtime", Format, StringComparison.OrdinalIgnoreCase)) + { + return new PrimaryType(KnownPrimaryType.UnixTime); + } return new PrimaryType(KnownPrimaryType.Int); case DataType.Boolean: return new PrimaryType(KnownPrimaryType.Boolean); diff --git a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime/Serialization/UnixTimeJsonConverter.cs b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime/Serialization/UnixTimeJsonConverter.cs new file mode 100644 index 0000000000000..8f6283b6237ce --- /dev/null +++ b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime/Serialization/UnixTimeJsonConverter.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Microsoft.Rest.Serialization +{ + public class UnixTimeJsonConverter : JsonConverter + { + public static readonly DateTime EpochDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + /// + /// Converts a byte array to a Base64Url encoded string + /// + /// The byte array to convert + /// The Base64Url encoded form of the input + private static long? ToUnixTime(DateTime dateTime) + { + return (long?)dateTime.Subtract(EpochDate).TotalSeconds; + } + + /// + /// Converts a Base64Url encoded string to a byte array + /// + /// The Base64Url encoded string + /// The byte array represented by the enconded string + private static DateTime FromUnixTime(long? seconds) + { + if (seconds.HasValue) + { + return EpochDate.AddSeconds(seconds.Value); + } + return EpochDate; + } + + public override bool CanConvert(Type objectType) + { + if (objectType == typeof(DateTime)) + return true; + + return false; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (objectType != typeof(DateTime)) + { + return serializer.Deserialize(reader, objectType); + } + else + { + var value = serializer.Deserialize(reader); + if (value.HasValue) + { + return FromUnixTime(value); + } + } + + return null; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value.GetType() != typeof(DateTime)) + { + JToken.FromObject(value).WriteTo(writer); + } + else + { + JToken.FromObject(ToUnixTime((DateTime)value)).WriteTo(writer); + } + } + } +} \ No newline at end of file From 166ff89b3daa50b4db041a182edec0595fd52b7b Mon Sep 17 00:00:00 2001 From: tbombach Date: Thu, 14 Apr 2016 00:03:36 -0700 Subject: [PATCH 02/16] Updating test server to add test scenarios for unix time --- .../CSharp/CSharp.Tests/AcceptanceTests.cs | 4 + .../AcceptanceTests/BodyInteger/IIntModel.cs | 42 ++ .../AcceptanceTests/BodyInteger/IntModel.cs | 467 ++++++++++++++++++ .../BodyInteger/IntModelExtensions.cs | 113 +++++ .../CompositeBoolIntClient/IIntModel.cs | 42 ++ .../CompositeBoolIntClient/IntModel.cs | 467 ++++++++++++++++++ .../IntModelExtensions.cs | 113 +++++ AutoRest/TestServer/server/routes/int.js | 19 +- AutoRest/TestServer/swagger/body-integer.json | 89 ++++ .../Serialization/UnixTimeJsonConverter.cs | 9 +- 10 files changed, 1360 insertions(+), 5 deletions(-) diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/AcceptanceTests.cs b/AutoRest/Generators/CSharp/CSharp.Tests/AcceptanceTests.cs index 0d5c31f0e5182..9caea8d8df283 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/AcceptanceTests.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/AcceptanceTests.cs @@ -198,12 +198,16 @@ public void IntegerTests() client.IntModel.PutMin32(Int32.MinValue); client.IntModel.PutMax64(Int64.MaxValue); client.IntModel.PutMin64(Int64.MinValue); + client.IntModel.PutUnixTimeDate(new DateTime(2016, 4, 13, 0, 0, 0)); client.IntModel.GetNull(); Assert.Throws(() => client.IntModel.GetInvalid()); Assert.Throws(() => client.IntModel.GetOverflowInt32()); Assert.Throws(() => client.IntModel.GetOverflowInt64()); Assert.Throws(() => client.IntModel.GetUnderflowInt32()); Assert.Throws(() => client.IntModel.GetUnderflowInt64()); + Assert.Throws(() => client.IntModel.GetInvalidUnixTime()); + Assert.Null(client.IntModel.GetNullUnixTime()); + Assert.Equal(new DateTime(2016, 4, 13, 0, 0, 0), client.IntModel.GetUnixTime()); } [Fact] diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IIntModel.cs b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IIntModel.cs index 4b90323fef26b..7131504bb1855 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IIntModel.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IIntModel.cs @@ -129,5 +129,47 @@ public partial interface IIntModel /// The cancellation token. /// Task PutMin64WithHttpMessagesAsync(long intBody, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Get datetime encoded as Unix time value + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task> GetUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Put datetime encoded as Unix time + /// + /// + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task PutUnixTimeDateWithHttpMessagesAsync(DateTime intBody, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Get invalid Unix time value + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task> GetInvalidUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Get null Unix time value + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task> GetNullUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IntModel.cs b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IntModel.cs index feb0d3013f373..9c9b57ebbb2a4 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IntModel.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IntModel.cs @@ -1202,5 +1202,472 @@ public IntModel(AutoRestIntegerTestService client) return _result; } + /// + /// Get datetime encoded as Unix time value + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> GetUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "GetUnixTime", tracingParameters); + } + // Construct URL + var _baseUrl = this.Client.BaseUri.AbsoluteUri; + var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "int/unixtime").ToString(); + // Create HTTP transport objects + HttpRequestMessage _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new Uri(_url); + // Set Headers + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new ErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + Error _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new HttpOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, new UnixTimeJsonConverter()); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// Put datetime encoded as Unix time + /// + /// + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task PutUnixTimeDateWithHttpMessagesAsync(DateTime intBody, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("intBody", intBody); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "PutUnixTimeDate", tracingParameters); + } + // Construct URL + var _baseUrl = this.Client.BaseUri.AbsoluteUri; + var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "int/unixtime").ToString(); + // Create HTTP transport objects + HttpRequestMessage _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("PUT"); + _httpRequest.RequestUri = new Uri(_url); + // Set Headers + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + if(intBody != null) + { + _requestContent = SafeJsonConvert.SerializeObject(intBody, new UnixTimeJsonConverter()); + _httpRequest.Content = new StringContent(_requestContent, Encoding.UTF8); + _httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new ErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + Error _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new HttpOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// Get invalid Unix time value + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> GetInvalidUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "GetInvalidUnixTime", tracingParameters); + } + // Construct URL + var _baseUrl = this.Client.BaseUri.AbsoluteUri; + var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "int/invalidunixtime").ToString(); + // Create HTTP transport objects + HttpRequestMessage _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new Uri(_url); + // Set Headers + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new ErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + Error _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new HttpOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, new UnixTimeJsonConverter()); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// Get null Unix time value + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> GetNullUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "GetNullUnixTime", tracingParameters); + } + // Construct URL + var _baseUrl = this.Client.BaseUri.AbsoluteUri; + var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "int/nullunixtime").ToString(); + // Create HTTP transport objects + HttpRequestMessage _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new Uri(_url); + // Set Headers + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new ErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + Error _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new HttpOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, new UnixTimeJsonConverter()); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + } } diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IntModelExtensions.cs b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IntModelExtensions.cs index 0c57e20b0aade..c0bc0812ebde8 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IntModelExtensions.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/BodyInteger/IntModelExtensions.cs @@ -305,5 +305,118 @@ public static void PutMin64(this IIntModel operations, long intBody) await operations.PutMin64WithHttpMessagesAsync(intBody, null, cancellationToken).ConfigureAwait(false); } + /// + /// Get datetime encoded as Unix time value + /// + /// + /// The operations group for this extension method. + /// + public static DateTime? GetUnixTime(this IIntModel operations) + { + return Task.Factory.StartNew(s => ((IIntModel)s).GetUnixTimeAsync(), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Get datetime encoded as Unix time value + /// + /// + /// The operations group for this extension method. + /// + /// + /// The cancellation token. + /// + public static async Task GetUnixTimeAsync(this IIntModel operations, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.GetUnixTimeWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + /// + /// Put datetime encoded as Unix time + /// + /// + /// The operations group for this extension method. + /// + /// + /// + public static void PutUnixTimeDate(this IIntModel operations, DateTime intBody) + { + Task.Factory.StartNew(s => ((IIntModel)s).PutUnixTimeDateAsync(intBody), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Put datetime encoded as Unix time + /// + /// + /// The operations group for this extension method. + /// + /// + /// + /// + /// The cancellation token. + /// + public static async Task PutUnixTimeDateAsync(this IIntModel operations, DateTime intBody, CancellationToken cancellationToken = default(CancellationToken)) + { + await operations.PutUnixTimeDateWithHttpMessagesAsync(intBody, null, cancellationToken).ConfigureAwait(false); + } + + /// + /// Get invalid Unix time value + /// + /// + /// The operations group for this extension method. + /// + public static DateTime? GetInvalidUnixTime(this IIntModel operations) + { + return Task.Factory.StartNew(s => ((IIntModel)s).GetInvalidUnixTimeAsync(), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Get invalid Unix time value + /// + /// + /// The operations group for this extension method. + /// + /// + /// The cancellation token. + /// + public static async Task GetInvalidUnixTimeAsync(this IIntModel operations, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.GetInvalidUnixTimeWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + /// + /// Get null Unix time value + /// + /// + /// The operations group for this extension method. + /// + public static DateTime? GetNullUnixTime(this IIntModel operations) + { + return Task.Factory.StartNew(s => ((IIntModel)s).GetNullUnixTimeAsync(), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Get null Unix time value + /// + /// + /// The operations group for this extension method. + /// + /// + /// The cancellation token. + /// + public static async Task GetNullUnixTimeAsync(this IIntModel operations, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.GetNullUnixTimeWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + } } diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IIntModel.cs b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IIntModel.cs index 4917e355224d7..d0b518478669c 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IIntModel.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IIntModel.cs @@ -129,5 +129,47 @@ public partial interface IIntModel /// The cancellation token. /// Task PutMin64WithHttpMessagesAsync(long intBody, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Get datetime encoded as Unix time value + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task> GetUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Put datetime encoded as Unix time + /// + /// + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task PutUnixTimeDateWithHttpMessagesAsync(DateTime intBody, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Get invalid Unix time value + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task> GetInvalidUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Get null Unix time value + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task> GetNullUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IntModel.cs b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IntModel.cs index 1ed42f86ca54e..2047fd860871e 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IntModel.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IntModel.cs @@ -1202,5 +1202,472 @@ public IntModel(CompositeBoolInt client) return _result; } + /// + /// Get datetime encoded as Unix time value + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> GetUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "GetUnixTime", tracingParameters); + } + // Construct URL + var _baseUrl = this.Client.BaseUri.AbsoluteUri; + var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "int/unixtime").ToString(); + // Create HTTP transport objects + HttpRequestMessage _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new Uri(_url); + // Set Headers + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new ErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + Error _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new HttpOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, new UnixTimeJsonConverter()); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// Put datetime encoded as Unix time + /// + /// + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task PutUnixTimeDateWithHttpMessagesAsync(DateTime intBody, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("intBody", intBody); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "PutUnixTimeDate", tracingParameters); + } + // Construct URL + var _baseUrl = this.Client.BaseUri.AbsoluteUri; + var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "int/unixtime").ToString(); + // Create HTTP transport objects + HttpRequestMessage _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("PUT"); + _httpRequest.RequestUri = new Uri(_url); + // Set Headers + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + if(intBody != null) + { + _requestContent = SafeJsonConvert.SerializeObject(intBody, new UnixTimeJsonConverter()); + _httpRequest.Content = new StringContent(_requestContent, Encoding.UTF8); + _httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new ErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + Error _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new HttpOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// Get invalid Unix time value + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> GetInvalidUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "GetInvalidUnixTime", tracingParameters); + } + // Construct URL + var _baseUrl = this.Client.BaseUri.AbsoluteUri; + var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "int/invalidunixtime").ToString(); + // Create HTTP transport objects + HttpRequestMessage _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new Uri(_url); + // Set Headers + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new ErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + Error _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new HttpOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, new UnixTimeJsonConverter()); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// Get null Unix time value + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> GetNullUnixTimeWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "GetNullUnixTime", tracingParameters); + } + // Construct URL + var _baseUrl = this.Client.BaseUri.AbsoluteUri; + var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "int/nullunixtime").ToString(); + // Create HTTP transport objects + HttpRequestMessage _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new Uri(_url); + // Set Headers + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new ErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + Error _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new HttpOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, new UnixTimeJsonConverter()); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + } } diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IntModelExtensions.cs b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IntModelExtensions.cs index a203d3cee39e7..009277ea5077b 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IntModelExtensions.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/CompositeBoolIntClient/IntModelExtensions.cs @@ -305,5 +305,118 @@ public static void PutMin64(this IIntModel operations, long intBody) await operations.PutMin64WithHttpMessagesAsync(intBody, null, cancellationToken).ConfigureAwait(false); } + /// + /// Get datetime encoded as Unix time value + /// + /// + /// The operations group for this extension method. + /// + public static DateTime? GetUnixTime(this IIntModel operations) + { + return Task.Factory.StartNew(s => ((IIntModel)s).GetUnixTimeAsync(), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Get datetime encoded as Unix time value + /// + /// + /// The operations group for this extension method. + /// + /// + /// The cancellation token. + /// + public static async Task GetUnixTimeAsync(this IIntModel operations, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.GetUnixTimeWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + /// + /// Put datetime encoded as Unix time + /// + /// + /// The operations group for this extension method. + /// + /// + /// + public static void PutUnixTimeDate(this IIntModel operations, DateTime intBody) + { + Task.Factory.StartNew(s => ((IIntModel)s).PutUnixTimeDateAsync(intBody), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Put datetime encoded as Unix time + /// + /// + /// The operations group for this extension method. + /// + /// + /// + /// + /// The cancellation token. + /// + public static async Task PutUnixTimeDateAsync(this IIntModel operations, DateTime intBody, CancellationToken cancellationToken = default(CancellationToken)) + { + await operations.PutUnixTimeDateWithHttpMessagesAsync(intBody, null, cancellationToken).ConfigureAwait(false); + } + + /// + /// Get invalid Unix time value + /// + /// + /// The operations group for this extension method. + /// + public static DateTime? GetInvalidUnixTime(this IIntModel operations) + { + return Task.Factory.StartNew(s => ((IIntModel)s).GetInvalidUnixTimeAsync(), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Get invalid Unix time value + /// + /// + /// The operations group for this extension method. + /// + /// + /// The cancellation token. + /// + public static async Task GetInvalidUnixTimeAsync(this IIntModel operations, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.GetInvalidUnixTimeWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + /// + /// Get null Unix time value + /// + /// + /// The operations group for this extension method. + /// + public static DateTime? GetNullUnixTime(this IIntModel operations) + { + return Task.Factory.StartNew(s => ((IIntModel)s).GetNullUnixTimeAsync(), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Get null Unix time value + /// + /// + /// The operations group for this extension method. + /// + /// + /// The cancellation token. + /// + public static async Task GetNullUnixTimeAsync(this IIntModel operations, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.GetNullUnixTimeWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + } } diff --git a/AutoRest/TestServer/server/routes/int.js b/AutoRest/TestServer/server/routes/int.js index 2299732ca5ad1..f4acbd7104cd2 100644 --- a/AutoRest/TestServer/server/routes/int.js +++ b/AutoRest/TestServer/server/routes/int.js @@ -65,10 +65,27 @@ var integer = function(coverage) { } else if (req.params.scenario === 'underflowint64') { coverage['getLongUnderflow']++; res.status(200).end('-9223372036854775910'); + } else if (req.params.scenario === 'unixtime') { + coverage['getUnixTime']++; + res.status(200).end('1460505600'); + } else if (req.params.scenario === 'invalidunixtime') { + coverage['getInvalidUnixTime']++; + res.status(200).end('123jkl'); + } else if (req.params.scenario === 'nullunixtime') { + coverage['getNullUnixTime']++; + res.status(200).end(); } else { res.status(400).send('Request path must contain true or false'); } - + }); + + router.put('/unixtime', function(req, res, next) { + if (req.body != 1460505600) { + utils.send400(res, next, "Did not like the value provided for unixtime in the req " + util.inspect(req.body)); + } else { + coverage['putUnixTime']++; + res.status(200).end(); + } }); } diff --git a/AutoRest/TestServer/swagger/body-integer.json b/AutoRest/TestServer/swagger/body-integer.json index 00b73a2c2731c..5f321e2981692 100644 --- a/AutoRest/TestServer/swagger/body-integer.json +++ b/AutoRest/TestServer/swagger/body-integer.json @@ -251,6 +251,95 @@ } } } + }, + "/int/unixtime": { + "get": { + "operationId": "int_getUnixTime", + "description": "Get datetime encoded as Unix time value", + "responses": { + "200": { + "description": "The date value encoded as Unix time", + "schema": { + "type": "integer", + "format": "unixtime" + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "put": { + "operationId": "int_putUnixTimeDate", + "description": "Put datetime encoded as Unix time", + "parameters": [ + { + "name": "intBody", + "in": "body", + "schema": { + "type": "integer", + "format": "unixtime" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "The datetime value encoded as Unix time" + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/int/invalidunixtime": { + "get": { + "operationId": "int_getInvalidUnixTime", + "description": "Get invalid Unix time value", + "responses": { + "200": { + "description": "The invalid Unix time value", + "schema": { + "type": "integer", + "format": "unixtime" + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/int/nullunixtime": { + "get": { + "operationId": "int_getNullUnixTime", + "description": "Get null Unix time value", + "responses": { + "200": { + "description": "The null Unix time value", + "schema": { + "type": "integer", + "format": "unixtime" + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } } }, "definitions": { diff --git a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime/Serialization/UnixTimeJsonConverter.cs b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime/Serialization/UnixTimeJsonConverter.cs index 8f6283b6237ce..fd345541a6de0 100644 --- a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime/Serialization/UnixTimeJsonConverter.cs +++ b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime/Serialization/UnixTimeJsonConverter.cs @@ -26,18 +26,18 @@ public class UnixTimeJsonConverter : JsonConverter /// /// The Base64Url encoded string /// The byte array represented by the enconded string - private static DateTime FromUnixTime(long? seconds) + private static DateTime? FromUnixTime(long? seconds) { if (seconds.HasValue) { return EpochDate.AddSeconds(seconds.Value); } - return EpochDate; + return null; } public override bool CanConvert(Type objectType) { - if (objectType == typeof(DateTime)) + if (objectType == typeof(DateTime?) || objectType == typeof(DateTime)) return true; return false; @@ -45,13 +45,14 @@ public override bool CanConvert(Type objectType) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - if (objectType != typeof(DateTime)) + if (objectType != typeof(DateTime?)) { return serializer.Deserialize(reader, objectType); } else { var value = serializer.Deserialize(reader); + if (value.HasValue) { return FromUnixTime(value); From 59d3fa94d2c1c186cf5a17d966376b61efec6bba Mon Sep 17 00:00:00 2001 From: tbombach Date: Thu, 14 Apr 2016 00:43:49 -0700 Subject: [PATCH 03/16] Adding test for Unix time encoded values in path --- .../CSharp/CSharp.Tests/AcceptanceTests.cs | 1 + .../Expected/AcceptanceTests/Url/IPaths.cs | 13 +++ .../Expected/AcceptanceTests/Url/Paths.cs | 106 ++++++++++++++++++ .../AcceptanceTests/Url/PathsExtensions.cs | 31 +++++ AutoRest/TestServer/server/routes/paths.js | 1 + AutoRest/TestServer/swagger/url.json | 30 +++++ 6 files changed, 182 insertions(+) diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/AcceptanceTests.cs b/AutoRest/Generators/CSharp/CSharp.Tests/AcceptanceTests.cs index 9caea8d8df283..804db915e6111 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/AcceptanceTests.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/AcceptanceTests.cs @@ -1355,6 +1355,7 @@ public void UrlPathTests() client.Paths.Base64Url(Encoding.UTF8.GetBytes("lorem")); var testArray = new List { "ArrayPath1", @"begin!*'();:@ &=+$,/?#[]end", null, "" }; client.Paths.ArrayCsvInPath(testArray); + client.Paths.UnixTimeUrl(new DateTime(2016, 4, 13, 0, 0, 0)); } } diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/IPaths.cs b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/IPaths.cs index ce60b9a5c2f28..d8f9e2c761579 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/IPaths.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/IPaths.cs @@ -305,5 +305,18 @@ public partial interface IPaths /// The cancellation token. /// Task ArrayCsvInPathWithHttpMessagesAsync(IList arrayPath, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Get the date 2016-04-13 encoded value as '1460505600' (Unix time) + /// + /// + /// Unix time encoded value + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task UnixTimeUrlWithHttpMessagesAsync(DateTime unixTimeUrlPath, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/Paths.cs b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/Paths.cs index d880804521f0c..3892f728ba11b 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/Paths.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/Paths.cs @@ -2692,5 +2692,111 @@ public Paths(AutoRestUrlTestService client) return _result; } + /// + /// Get the date 2016-04-13 encoded value as '1460505600' (Unix time) + /// + /// + /// Unix time encoded value + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task UnixTimeUrlWithHttpMessagesAsync(DateTime unixTimeUrlPath, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("unixTimeUrlPath", unixTimeUrlPath); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "UnixTimeUrl", tracingParameters); + } + // Construct URL + var _baseUrl = this.Client.BaseUri.AbsoluteUri; + var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "paths/int/1460505600/{unixTimeUrlPath}").ToString(); + _url = _url.Replace("{unixTimeUrlPath}", Uri.EscapeDataString(SafeJsonConvert.SerializeObject(unixTimeUrlPath, new UnixTimeJsonConverter()).Trim('"'))); + // Create HTTP transport objects + HttpRequestMessage _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new Uri(_url); + // Set Headers + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new ErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + Error _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new HttpOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + } } diff --git a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/PathsExtensions.cs b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/PathsExtensions.cs index 65ea07ea61b8b..b4e8d2ce268e6 100644 --- a/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/PathsExtensions.cs +++ b/AutoRest/Generators/CSharp/CSharp.Tests/Expected/AcceptanceTests/Url/PathsExtensions.cs @@ -712,5 +712,36 @@ public static void ArrayCsvInPath(this IPaths operations, IList arrayPat await operations.ArrayCsvInPathWithHttpMessagesAsync(arrayPath, null, cancellationToken).ConfigureAwait(false); } + /// + /// Get the date 2016-04-13 encoded value as '1460505600' (Unix time) + /// + /// + /// The operations group for this extension method. + /// + /// + /// Unix time encoded value + /// + public static void UnixTimeUrl(this IPaths operations, DateTime unixTimeUrlPath) + { + Task.Factory.StartNew(s => ((IPaths)s).UnixTimeUrlAsync(unixTimeUrlPath), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Get the date 2016-04-13 encoded value as '1460505600' (Unix time) + /// + /// + /// The operations group for this extension method. + /// + /// + /// Unix time encoded value + /// + /// + /// The cancellation token. + /// + public static async Task UnixTimeUrlAsync(this IPaths operations, DateTime unixTimeUrlPath, CancellationToken cancellationToken = default(CancellationToken)) + { + await operations.UnixTimeUrlWithHttpMessagesAsync(unixTimeUrlPath, null, cancellationToken).ConfigureAwait(false); + } + } } diff --git a/AutoRest/TestServer/server/routes/paths.js b/AutoRest/TestServer/server/routes/paths.js index ba1b8e99df9e6..04b5002453c3c 100644 --- a/AutoRest/TestServer/server/routes/paths.js +++ b/AutoRest/TestServer/server/routes/paths.js @@ -23,6 +23,7 @@ var scenarioMap = { "2012-01-01T01:01:01Z": "Valid", "green color" : "Valid", "bG9yZW0" : "Base64Url", + "1460505600": "UnixTime", "ArrayPath1,begin!*'();:@ &=+$,/?#[]end,,": "CSVInPath" }; diff --git a/AutoRest/TestServer/swagger/url.json b/AutoRest/TestServer/swagger/url.json index 4bf4530d177fb..670ff00f9bd1d 100644 --- a/AutoRest/TestServer/swagger/url.json +++ b/AutoRest/TestServer/swagger/url.json @@ -773,6 +773,36 @@ } } }, + "/paths/int/1460505600/{unixTimeUrlPath}": { + "get": { + "operationId": "paths_unixTimeUrl", + "description": "Get the date 2016-04-13 encoded value as '1460505600' (Unix time)", + "tags": [ + "Path Operations" + ], + "parameters": [ + { + "name": "unixTimeUrlPath", + "in": "path", + "description": "Unix time encoded value", + "type": "integer", + "format": "unixtime", + "required": true + } + ], + "responses": { + "200": { + "description": "Successfully Received date 2016-04-13 encoded value as '1460505600' (Unix time)" + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, "/queries/bool/true": { "get": { "operationId": "queries_getBooleanTrue", From 557787508985b3e75e99defbfedf991989963ab1 Mon Sep 17 00:00:00 2001 From: tbombach Date: Thu, 14 Apr 2016 00:51:57 -0700 Subject: [PATCH 04/16] Updating test coverage list --- AutoRest/TestServer/server/app.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/AutoRest/TestServer/server/app.js b/AutoRest/TestServer/server/app.js index 8bb3b105ba765..9453e73965c22 100644 --- a/AutoRest/TestServer/server/app.js +++ b/AutoRest/TestServer/server/app.js @@ -195,15 +195,6 @@ var coverage = { "getStringWithLeadingAndTrailingWhitespace" : 0, "putStringWithLeadingAndTrailingWhitespace" : 0, "getStringNotProvided": 0, - /* TODO: only C# and node.js support the base64url format currently. Exclude these tests from code coverage until it is implemented in other languages */ - "getStringBase64Encoded": 1, - "getStringBase64UrlEncoded": 1, - "putStringBase64UrlEncoded": 1, - "getStringNullBase64UrlEncoding": 1, - "getArrayBase64Url": 1, - "getDictionaryBase64Url": 1, - "UrlPathsStringBase64Url": 1, - "UrlPathsArrayCSVInPath": 1, "getEnumNotExpandable": 0, "putEnumNotExpandable":0, "putComplexBasicValid": 0, @@ -438,7 +429,22 @@ var coverage = { 'putModelFlattenResourceCollection': 0, 'putModelFlattenCustomBase': 0, 'postModelFlattenCustomParameter': 0, - 'putModelFlattenCustomGroupedParameter': 0 + 'putModelFlattenCustomGroupedParameter': 0, + /* TODO: only C# and node.js support the base64url format currently. Exclude these tests from code coverage until it is implemented in other languages */ + "getStringBase64Encoded": 1, + "getStringBase64UrlEncoded": 1, + "putStringBase64UrlEncoded": 1, + "getStringNullBase64UrlEncoding": 1, + "getArrayBase64Url": 1, + "getDictionaryBase64Url": 1, + "UrlPathsStringBase64Url": 1, + "UrlPathsArrayCSVInPath": 1, + /* TODO: only C# supports the unixtime format currently. Exclude these tests from code coverage until it is implemented in other languages */ + "getUnixTime": 1, + "getInvalidUnixTime": 1, + "getNullUnixTime": 1, + "putUnixTime": 1, + "UrlPathsIntUnixTime": 1 }; // view engine setup From b6bf4ccfc3413a2ea50ad23c54b71e9c495e3b04 Mon Sep 17 00:00:00 2001 From: aescribano Date: Thu, 14 Apr 2016 11:57:30 +0100 Subject: [PATCH 05/16] Adding a User-Agent header to the request done in FileSystem.ReadFileAsText --- AutoRest/AutoRest.Core/Utilities/FileSystem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/AutoRest/AutoRest.Core/Utilities/FileSystem.cs b/AutoRest/AutoRest.Core/Utilities/FileSystem.cs index 91b4f6c18bdd0..231b6521090d3 100644 --- a/AutoRest/AutoRest.Core/Utilities/FileSystem.cs +++ b/AutoRest/AutoRest.Core/Utilities/FileSystem.cs @@ -17,6 +17,7 @@ public string ReadFileAsText(string path) { using (var client = new WebClient()) { + client.Headers.Add("User-Agent: AutoRest"); return client.DownloadString(path); } } From 8b015ac266ddb655991927ac6a14c454d9e31b1d Mon Sep 17 00:00:00 2001 From: John-Hart Date: Thu, 14 Apr 2016 13:05:37 -0700 Subject: [PATCH 06/16] Captured the JSONException that may occur on a Non-Success StatusCode when attempting to Deserialize the HTTPResponse.Content when it is not JSON --- .../AzureClientExtensions.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure/AzureClientExtensions.cs b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure/AzureClientExtensions.cs index 57bbd064b95c2..0d8505210fd5e 100644 --- a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure/AzureClientExtensions.cs +++ b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure/AzureClientExtensions.cs @@ -599,7 +599,16 @@ private static async Task> GetRawAsync( statusCode != HttpStatusCode.Created && statusCode != HttpStatusCode.NoContent) { - CloudError errorBody = SafeJsonConvert.DeserializeObject(responseContent, client.DeserializationSettings); + CloudError errorBody = null; + try + { + errorBody = SafeJsonConvert.DeserializeObject(responseContent, client.DeserializationSettings); + } + catch (JsonException) + { + // failed to deserialize, return empty body + } + throw new CloudException(string.Format(CultureInfo.InvariantCulture, Resources.LongRunningOperationFailed, statusCode)) { From 3622e2bc43b1b387b4c89a9b9b0a7d8a89622e8f Mon Sep 17 00:00:00 2001 From: John-Hart Date: Thu, 14 Apr 2016 13:12:05 -0700 Subject: [PATCH 07/16] Added a new LongRunningOperations test to verify that a CloudException is thrown even when a JSONException occurs Deserializing the HTTPResponse.Content --- .../LongRunningOperationsTest.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/LongRunningOperationsTest.cs b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/LongRunningOperationsTest.cs index bf999fe9f7c1b..1d0bb2069f1bf 100644 --- a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/LongRunningOperationsTest.cs +++ b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/LongRunningOperationsTest.cs @@ -104,6 +104,19 @@ public void TestAsyncOperationWithMissingProvisioningState() Assert.Equal("100", resource.Id); } + [Fact] + public void TestAsyncOperationWithNonSuccessStatusAndInvalidResponseContent() + { + var tokenCredentials = new TokenCredentials("123", "abc"); + var handler = new PlaybackTestHandler(MockAsyncOperaionWithNonSuccessStatusAndInvalidResponseContent()); + var fakeClient = new RedisManagementClient(tokenCredentials, handler); + fakeClient.LongRunningOperationInitialTimeout = fakeClient.LongRunningOperationRetryTimeout = 0; + var error = Assert.Throws(() => + fakeClient.RedisOperations.Delete("rg", "redis", "1234")); + Assert.Equal("Long running operation failed with status 'BadRequest'.", error.Message); + Assert.Null(error.Body); + } + [Fact] public void TestPutOperationWithoutProvisioningState() { @@ -737,6 +750,22 @@ private IEnumerable MockAsyncOperaionWithMissingProvisionin yield return response3; } + private IEnumerable MockAsyncOperaionWithNonSuccessStatusAndInvalidResponseContent() + { + var response1 = new HttpResponseMessage(HttpStatusCode.Accepted) + { + Content = new StringContent("") + }; + response1.Headers.Add("Location", "http://custom/status"); + yield return response1; + + var response2 = new HttpResponseMessage(HttpStatusCode.BadRequest) + { + Content = new StringContent("<") + }; + yield return response2; + } + private IEnumerable MockPutOperaionWithoutProvisioningStateInResponse() { var response1 = new HttpResponseMessage(HttpStatusCode.Created) From d41d44c1d80f10460a8cc8d4d4811f971407ead2 Mon Sep 17 00:00:00 2001 From: John-Hart Date: Tue, 19 Apr 2016 13:12:02 -0700 Subject: [PATCH 08/16] Added a LRO acceptance test to verify that a CloudException is thrown when a JSONException occurs during Deserialization of the response content --- .../Azure.CSharp.Tests/AcceptanceTests.cs | 4 + .../AcceptanceTests/Lro/ILROSADsOperations.cs | 28 +++ .../AcceptanceTests/Lro/LROSADsOperations.cs | 203 ++++++++++++++++++ .../Lro/LROSADsOperationsExtensions.cs | 72 +++++++ AutoRest/TestServer/server/routes/lros.js | 10 + AutoRest/TestServer/swagger/lro.json | 40 ++++ 6 files changed, 357 insertions(+) diff --git a/AutoRest/Generators/CSharp/Azure.CSharp.Tests/AcceptanceTests.cs b/AutoRest/Generators/CSharp/Azure.CSharp.Tests/AcceptanceTests.cs index d04c712fd1c96..46078380b3b9a 100644 --- a/AutoRest/Generators/CSharp/Azure.CSharp.Tests/AcceptanceTests.cs +++ b/AutoRest/Generators/CSharp/Azure.CSharp.Tests/AcceptanceTests.cs @@ -274,6 +274,10 @@ public void LroSadPathTests() Assert.Equal("Error from the server", exception.Body.Message); Assert.NotNull(exception.Request); Assert.NotNull(exception.Response); + exception = + Assert.Throws(() => client.LROSADs.PutNonRetry201Creating400InvalidJson(new Product { Location = "West US" })); + Assert.Null(exception.Body); + Assert.Equal("Long running operation failed with status 'BadRequest'.", exception.Message); exception = Assert.Throws( () => client.LROSADs.PutAsyncRelativeRetry400(new Product {Location = "West US"})); diff --git a/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/ILROSADsOperations.cs b/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/ILROSADsOperations.cs index 60ac71a045d3e..5717f7f8f64e8 100644 --- a/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/ILROSADsOperations.cs +++ b/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/ILROSADsOperations.cs @@ -79,6 +79,34 @@ public partial interface ILROSADsOperations /// Task> BeginPutNonRetry201Creating400WithHttpMessagesAsync(Product product = default(Product), Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); /// + /// Long running put request, service returns a Product with + /// 'ProvisioningState' = 'Creating' and 201 response code + /// + /// + /// Product to put + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task> PutNonRetry201Creating400InvalidJsonWithHttpMessagesAsync(Product product = default(Product), Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Long running put request, service returns a Product with + /// 'ProvisioningState' = 'Creating' and 201 response code + /// + /// + /// Product to put + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + Task> BeginPutNonRetry201Creating400InvalidJsonWithHttpMessagesAsync(Product product = default(Product), Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// /// Long running put request, service returns a 200 with /// ProvisioningState=’Creating’. Poll the endpoint indicated in the /// Azure-AsyncOperation header for operation status diff --git a/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/LROSADsOperations.cs b/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/LROSADsOperations.cs index 7b0662de0c29e..cbd54f78112ae 100644 --- a/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/LROSADsOperations.cs +++ b/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/LROSADsOperations.cs @@ -453,6 +453,209 @@ internal LROSADsOperations(AutoRestLongRunningOperationTestService client) return _result; } + /// + /// Long running put request, service returns a Product with + /// 'ProvisioningState' = 'Creating' and 201 response code + /// + /// + /// Product to put + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + public async Task> PutNonRetry201Creating400InvalidJsonWithHttpMessagesAsync(Product product = default(Product), Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Send Request + AzureOperationResponse _response = await BeginPutNonRetry201Creating400InvalidJsonWithHttpMessagesAsync( + product, customHeaders, cancellationToken); + return await this.Client.GetPutOrPatchOperationResultAsync(_response, + customHeaders, + cancellationToken); + } + + /// + /// Long running put request, service returns a Product with + /// 'ProvisioningState' = 'Creating' and 201 response code + /// + /// + /// Product to put + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> BeginPutNonRetry201Creating400InvalidJsonWithHttpMessagesAsync(Product product = default(Product), Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("product", product); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "BeginPutNonRetry201Creating400InvalidJson", tracingParameters); + } + // Construct URL + var _baseUrl = this.Client.BaseUri.AbsoluteUri; + var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "lro/nonretryerror/put/201/creating/400/invalidjson").ToString(); + List _queryParameters = new List(); + if (_queryParameters.Count > 0) + { + _url += "?" + string.Join("&", _queryParameters); + } + // Create HTTP transport objects + HttpRequestMessage _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("PUT"); + _httpRequest.RequestUri = new Uri(_url); + // Set Headers + if (this.Client.GenerateClientRequestId != null && this.Client.GenerateClientRequestId.Value) + { + _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", Guid.NewGuid().ToString()); + } + if (this.Client.AcceptLanguage != null) + { + if (_httpRequest.Headers.Contains("accept-language")) + { + _httpRequest.Headers.Remove("accept-language"); + } + _httpRequest.Headers.TryAddWithoutValidation("accept-language", this.Client.AcceptLanguage); + } + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + if(product != null) + { + _requestContent = SafeJsonConvert.SerializeObject(product, this.Client.SerializationSettings); + _httpRequest.Content = new StringContent(_requestContent, Encoding.UTF8); + _httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + } + // Set Credentials + if (this.Client.Credentials != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await this.Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await this.Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200 && (int)_statusCode != 201) + { + var ex = new CloudException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + CloudError _errorBody = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + if (_errorBody != null) + { + ex = new CloudException(_errorBody.Message); + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + ex.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new AzureOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + // Deserialize Response + if ((int)_statusCode == 201) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = SafeJsonConvert.DeserializeObject(_responseContent, this.Client.DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + /// /// Long running put request, service returns a 200 with /// ProvisioningState=’Creating’. Poll the endpoint indicated in the diff --git a/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/LROSADsOperationsExtensions.cs b/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/LROSADsOperationsExtensions.cs index 86b6c1b083fc9..dc7d177cbe402 100644 --- a/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/LROSADsOperationsExtensions.cs +++ b/AutoRest/Generators/CSharp/Azure.CSharp.Tests/Expected/AcceptanceTests/Lro/LROSADsOperationsExtensions.cs @@ -162,6 +162,78 @@ public static partial class LROSADsOperationsExtensions } } + /// + /// Long running put request, service returns a Product with + /// 'ProvisioningState' = 'Creating' and 201 response code + /// + /// + /// The operations group for this extension method. + /// + /// + /// Product to put + /// + public static Product PutNonRetry201Creating400InvalidJson(this ILROSADsOperations operations, Product product = default(Product)) + { + return Task.Factory.StartNew(s => ((ILROSADsOperations)s).PutNonRetry201Creating400InvalidJsonAsync(product), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Long running put request, service returns a Product with + /// 'ProvisioningState' = 'Creating' and 201 response code + /// + /// + /// The operations group for this extension method. + /// + /// + /// Product to put + /// + /// + /// The cancellation token. + /// + public static async Task PutNonRetry201Creating400InvalidJsonAsync(this ILROSADsOperations operations, Product product = default(Product), CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.PutNonRetry201Creating400InvalidJsonWithHttpMessagesAsync(product, null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + /// + /// Long running put request, service returns a Product with + /// 'ProvisioningState' = 'Creating' and 201 response code + /// + /// + /// The operations group for this extension method. + /// + /// + /// Product to put + /// + public static Product BeginPutNonRetry201Creating400InvalidJson(this ILROSADsOperations operations, Product product = default(Product)) + { + return Task.Factory.StartNew(s => ((ILROSADsOperations)s).BeginPutNonRetry201Creating400InvalidJsonAsync(product), operations, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult(); + } + + /// + /// Long running put request, service returns a Product with + /// 'ProvisioningState' = 'Creating' and 201 response code + /// + /// + /// The operations group for this extension method. + /// + /// + /// Product to put + /// + /// + /// The cancellation token. + /// + public static async Task BeginPutNonRetry201Creating400InvalidJsonAsync(this ILROSADsOperations operations, Product product = default(Product), CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.BeginPutNonRetry201Creating400InvalidJsonWithHttpMessagesAsync(product, null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + /// /// Long running put request, service returns a 200 with /// ProvisioningState=’Creating’. Poll the endpoint indicated in the diff --git a/AutoRest/TestServer/server/routes/lros.js b/AutoRest/TestServer/server/routes/lros.js index 9d9dc540e16aa..1f896de2765cd 100644 --- a/AutoRest/TestServer/server/routes/lros.js +++ b/AutoRest/TestServer/server/routes/lros.js @@ -940,6 +940,16 @@ var lros = function (coverage) { res.status(400).end('{ "message" : "Error from the server" }'); }); + coverage['LRONonRetryPut201Creating400InvalidJson'] = 0; + router.put('/nonretryerror/put/201/creating/400/invalidjson', function (req, res, next) { + res.status(201).end('{ "properties": { "provisioningState": "Creating"}, "id": "100", "name": "foo" }'); + }); + + router.get('/nonretryerror/put/201/creating/400/invalidjson', function (req, res, next) { + coverage['LRONonRetryPut201Creating400InvalidJson']++; + res.status(400).end('<{ "message" : "Error from the server" }'); + }); + coverage['LRONonRetryPutAsyncRetry400'] = 0; router.put('/nonretryerror/putasync/retry/400', function (req, res, next) { var pollingUri = 'http://localhost.:' + utils.getPort() + '/lro/nonretryerror/putasync/retry/failed/operationResults/400'; diff --git a/AutoRest/TestServer/swagger/lro.json b/AutoRest/TestServer/swagger/lro.json index b064ce8c3aa2d..9f9bba0c26759 100644 --- a/AutoRest/TestServer/swagger/lro.json +++ b/AutoRest/TestServer/swagger/lro.json @@ -1772,6 +1772,46 @@ } } } + }, + "/lro/nonretryerror/put/201/creating/400/invalidjson": { + "put": { + "x-ms-long-running-operation": true, + "operationId": "LROSADs_putNonRetry201Creating400InvalidJson", + "description": "Long running put request, service returns a Product with 'ProvisioningState' = 'Creating' and 201 response code", + "tags": [ + "LROSAD Operations" + ], + "parameters": [ + { + "name": "product", + "description": "Product to put", + "in": "body", + "schema": { + "$ref": "#/definitions/Product" + } + } + ], + "responses": { + "200": { + "description": "Response after completion, with ProvisioningState='Succeeded'", + "schema": { + "$ref": "#/definitions/Product" + } + }, + "201": { + "description": "Initial response, with ProvisioningState = 'Creating'", + "schema": { + "$ref": "#/definitions/Product" + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/CloudError" + } + } + } + } }, "/lro/nonretryerror/putasync/retry/400": { "put": { From 16b8f29cfba5ff953897b641680844c3c98928af Mon Sep 17 00:00:00 2001 From: John-Hart Date: Tue, 19 Apr 2016 17:13:11 -0700 Subject: [PATCH 09/16] Set the intial coverage for the LRONonRetryPut201Creating400InvalidJson to 1 --- AutoRest/TestServer/server/routes/lros.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AutoRest/TestServer/server/routes/lros.js b/AutoRest/TestServer/server/routes/lros.js index 1f896de2765cd..ca11fa50082be 100644 --- a/AutoRest/TestServer/server/routes/lros.js +++ b/AutoRest/TestServer/server/routes/lros.js @@ -940,7 +940,8 @@ var lros = function (coverage) { res.status(400).end('{ "message" : "Error from the server" }'); }); - coverage['LRONonRetryPut201Creating400InvalidJson'] = 0; + /* TODO: only C# has implemented this test. Exclude it from code coverage until it is implemented in other languages */ + coverage['LRONonRetryPut201Creating400InvalidJson'] = 1; router.put('/nonretryerror/put/201/creating/400/invalidjson', function (req, res, next) { res.status(201).end('{ "properties": { "provisioningState": "Creating"}, "id": "100", "name": "foo" }'); }); From dccbc73bdcd6ce3bd95e22b44d03e40b945fbeff Mon Sep 17 00:00:00 2001 From: tbombach Date: Thu, 21 Apr 2016 12:37:03 -0700 Subject: [PATCH 10/16] Simplifying the logic for determining SerializationSettings Removing duplicated and hard-to-read code that determined the C# serialization/deserialization settings to be used for an IType. Changes are needed because adding new checks for known primary types was failing builds based cyclomatic complexity > 25. This change adds extension methods that determine if an IType is either a PrimaryType for a given KnownPrimaryType, or a DictionaryType/SequenceType that contains values of that known type. --- .../AutoRest.Core/Utilities/Extensions.cs | 51 ++++++++++++++++++- .../TemplateModels/MethodTemplateModel.cs | 47 +++-------------- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/AutoRest/AutoRest.Core/Utilities/Extensions.cs b/AutoRest/AutoRest.Core/Utilities/Extensions.cs index 23bea5173d9db..9a18e21f1f5b4 100644 --- a/AutoRest/AutoRest.Core/Utilities/Extensions.cs +++ b/AutoRest/AutoRest.Core/Utilities/Extensions.cs @@ -244,7 +244,7 @@ public static string EscapeXmlComment(this string comment) } /// - /// Returns true is the type is a PrimaryType with KnownPrimaryType matching typeToMatch. + /// Returns true if the type is a PrimaryType with KnownPrimaryType matching typeToMatch. /// /// /// @@ -263,5 +263,54 @@ public static bool IsPrimaryType(this IType type, KnownPrimaryType typeToMatch) } return false; } + + /// + /// Returns true if the is a PrimaryType with KnownPrimaryType matching + /// or a DictionaryType with ValueType matching or a SequenceType matching + /// + /// + /// + /// + public static bool IsOrContainsPrimaryType(this IType type, KnownPrimaryType typeToMatch) + { + if (type == null) + { + return false; + } + + if (type.IsPrimaryType(typeToMatch) || + type.IsDictionaryContainingType(typeToMatch) || + type.IsSequenceContainingType(typeToMatch)) + { + return true; + } + return false; + } + + /// + /// Returns true if the is a DictionaryType with ValueType matching + /// + /// + /// + /// + public static bool IsDictionaryContainingType(this IType type, KnownPrimaryType typeToMatch) + { + DictionaryType dictionaryType = type as DictionaryType; + PrimaryType dictionaryPrimaryType = dictionaryType?.ValueType as PrimaryType; + return dictionaryPrimaryType != null && dictionaryPrimaryType.IsPrimaryType(typeToMatch); + } + + /// + /// Returns true if the is a SequenceType matching + /// + /// + /// + /// + public static bool IsSequenceContainingType(this IType type, KnownPrimaryType typeToMatch) + { + SequenceType sequenceType = type as SequenceType; + PrimaryType sequencePrimaryType = sequenceType?.ElementType as PrimaryType; + return sequencePrimaryType != null && sequencePrimaryType.IsPrimaryType(typeToMatch); + } } } \ No newline at end of file diff --git a/AutoRest/Generators/CSharp/CSharp/TemplateModels/MethodTemplateModel.cs b/AutoRest/Generators/CSharp/CSharp/TemplateModels/MethodTemplateModel.cs index d4a380df2765c..22d1ca54614d0 100644 --- a/AutoRest/Generators/CSharp/CSharp/TemplateModels/MethodTemplateModel.cs +++ b/AutoRest/Generators/CSharp/CSharp/TemplateModels/MethodTemplateModel.cs @@ -325,37 +325,19 @@ public string ClientReference /// public string GetSerializationSettingsReference(IType serializationType) { - SequenceType sequenceType = serializationType as SequenceType; - DictionaryType dictionaryType = serializationType as DictionaryType; - if (serializationType.IsPrimaryType(KnownPrimaryType.Date) || - (sequenceType != null && sequenceType.ElementType is PrimaryType - && ((PrimaryType)sequenceType.ElementType).Type == KnownPrimaryType.Date) || - (dictionaryType != null && dictionaryType.ValueType is PrimaryType - && ((PrimaryType)dictionaryType.ValueType).Type == KnownPrimaryType.Date)) + if (serializationType.IsOrContainsPrimaryType(KnownPrimaryType.Date)) { return "new DateJsonConverter()"; } - else if (serializationType.IsPrimaryType(KnownPrimaryType.DateTimeRfc1123) || - (sequenceType != null && sequenceType.ElementType is PrimaryType - && ((PrimaryType)sequenceType.ElementType).Type == KnownPrimaryType.DateTimeRfc1123) || - (dictionaryType != null && dictionaryType.ValueType is PrimaryType - && ((PrimaryType)dictionaryType.ValueType).Type == KnownPrimaryType.DateTimeRfc1123)) + else if (serializationType.IsOrContainsPrimaryType(KnownPrimaryType.DateTimeRfc1123)) { return "new DateTimeRfc1123JsonConverter()"; } - else if (serializationType.IsPrimaryType(KnownPrimaryType.Base64Url) || - (sequenceType != null && sequenceType.ElementType is PrimaryType - && ((PrimaryType)sequenceType.ElementType).Type == KnownPrimaryType.Base64Url) || - (dictionaryType != null && dictionaryType.ValueType is PrimaryType - && ((PrimaryType)dictionaryType.ValueType).Type == KnownPrimaryType.Base64Url)) + else if (serializationType.IsOrContainsPrimaryType(KnownPrimaryType.Base64Url)) { return "new Base64UrlJsonConverter()"; } - else if (serializationType.IsPrimaryType(KnownPrimaryType.UnixTime) || - (sequenceType != null && sequenceType.ElementType is PrimaryType - && ((PrimaryType)sequenceType.ElementType).Type == KnownPrimaryType.UnixTime) || - (dictionaryType != null && dictionaryType.ValueType is PrimaryType - && ((PrimaryType)dictionaryType.ValueType).Type == KnownPrimaryType.UnixTime)) + else if (serializationType.IsOrContainsPrimaryType(KnownPrimaryType.UnixTime)) { return "new UnixTimeJsonConverter()"; } @@ -369,33 +351,18 @@ public string GetSerializationSettingsReference(IType serializationType) /// public string GetDeserializationSettingsReference(IType deserializationType) { - SequenceType sequenceType = deserializationType as SequenceType; - DictionaryType dictionaryType = deserializationType as DictionaryType; - if (deserializationType.IsPrimaryType(KnownPrimaryType.Date) || - (sequenceType != null && sequenceType.ElementType is PrimaryType - && ((PrimaryType)sequenceType.ElementType).Type == KnownPrimaryType.Date) || - (dictionaryType != null && dictionaryType.ValueType is PrimaryType - && ((PrimaryType)dictionaryType.ValueType).Type == KnownPrimaryType.Date)) + if (deserializationType.IsOrContainsPrimaryType(KnownPrimaryType.Date)) { return "new DateJsonConverter()"; } - else if (deserializationType.IsPrimaryType(KnownPrimaryType.Base64Url) || - (sequenceType != null && sequenceType.ElementType is PrimaryType - && ((PrimaryType)sequenceType.ElementType).Type == KnownPrimaryType.Base64Url) || - (dictionaryType != null && dictionaryType.ValueType is PrimaryType - && ((PrimaryType)dictionaryType.ValueType).Type == KnownPrimaryType.Base64Url)) + else if (deserializationType.IsOrContainsPrimaryType(KnownPrimaryType.Base64Url)) { return "new Base64UrlJsonConverter()"; } - else if (deserializationType.IsPrimaryType(KnownPrimaryType.UnixTime) || - (sequenceType != null && sequenceType.ElementType is PrimaryType - && ((PrimaryType)sequenceType.ElementType).Type == KnownPrimaryType.UnixTime) || - (dictionaryType != null && dictionaryType.ValueType is PrimaryType - && ((PrimaryType)dictionaryType.ValueType).Type == KnownPrimaryType.UnixTime)) + else if (deserializationType.IsOrContainsPrimaryType(KnownPrimaryType.UnixTime)) { return "new UnixTimeJsonConverter()"; } - return ClientReference + ".DeserializationSettings"; } From 468c8c4e0e8665a47708901c21cbfe25b4a1a863 Mon Sep 17 00:00:00 2001 From: John Hart Date: Fri, 22 Apr 2016 13:31:47 -0700 Subject: [PATCH 11/16] Fixes the issue where the contents of the output file for SingleFileGeneration are appended to each time Autorest is run (#968) * Deleted the file specified in the OutputFileName the before writing to it the first time. * Updated OutputToSingleFile test to verify contents are overwritten * Corrected the new Assert in OutputToSingFile test --- .../CodeGeneratorsTests.cs | 3 +++ AutoRest/AutoRest.Core/CodeGenerator.cs | 21 +++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/AutoRest/AutoRest.Core.Tests/CodeGeneratorsTests.cs b/AutoRest/AutoRest.Core.Tests/CodeGeneratorsTests.cs index c58c81e4e6b0c..8cadb3f6414f4 100644 --- a/AutoRest/AutoRest.Core.Tests/CodeGeneratorsTests.cs +++ b/AutoRest/AutoRest.Core.Tests/CodeGeneratorsTests.cs @@ -86,8 +86,11 @@ public void OutputToSingleFile() }; string path = Path.Combine(settings.OutputDirectory, "test.file.cs"); + string existingContents = "this is dummy"; + _fileSystem.VirtualStore[path] = new StringBuilder(existingContents); var codeGenerator = new SampleCodeGenerator(settings); codeGenerator.Generate(new ServiceClient()).GetAwaiter().GetResult(); + Assert.DoesNotContain(existingContents, _fileSystem.VirtualStore[path].ToString()); Assert.Equal(4, _fileSystem.VirtualStore.Count); Assert.True(_fileSystem.VirtualStore.ContainsKey(path)); Assert.True(_fileSystem.VirtualStore.ContainsKey("AutoRest.json")); diff --git a/AutoRest/AutoRest.Core/CodeGenerator.cs b/AutoRest/AutoRest.Core/CodeGenerator.cs index 8efb1c0311361..57dbecb697346 100644 --- a/AutoRest/AutoRest.Core/CodeGenerator.cs +++ b/AutoRest/AutoRest.Core/CodeGenerator.cs @@ -16,6 +16,7 @@ namespace Microsoft.Rest.Generator public abstract class CodeGenerator { public const string EnumObject = "x-ms-enum"; + private bool firstTimeWriteSingleFile = true; protected CodeGenerator(Settings settings) { @@ -99,7 +100,7 @@ public async Task Write(ITemplate template, string fileName) /// public async Task Write(string template, string fileName) { - string relativeFilePath = null; + string filePath = null; if (Settings.OutputFileName != null) { @@ -110,17 +111,19 @@ public async Task Write(string template, string fileName) ErrorManager.ThrowErrors(); } - relativeFilePath = Settings.OutputFileName; + filePath = Path.Combine(Settings.OutputDirectory, Settings.OutputFileName); + + if (firstTimeWriteSingleFile) + { + // for SingleFileGeneration clean the file before writing only if its the first time + Settings.FileSystem.DeleteFile(filePath); + firstTimeWriteSingleFile = false; + } } else { - relativeFilePath = fileName; - } - string filePath = Path.Combine(Settings.OutputDirectory, relativeFilePath); - - // cleans file before writing unless single file - if (!(Settings.OutputFileName != null && IsSingleFileGenerationSupported)) - { + filePath = Path.Combine(Settings.OutputDirectory, fileName); + // cleans file before writing Settings.FileSystem.DeleteFile(filePath); } // Make sure the directory exist From d1d781c39695423fb04e8f37933b39f204a6cffe Mon Sep 17 00:00:00 2001 From: annatisch Date: Fri, 22 Apr 2016 13:32:59 -0700 Subject: [PATCH 12/16] Python Read-only and UTF-8 support (#959) * Test server debuging * removed stream connection closing * Revised serialization to support readonly attributes * Updated generator for better readonly attributes * Updated tests * Regenerated tests * Removed explicit read-only parameters * Updated readonly docstrings * Removed properties * enforced utf-8 encoding * Updated generated docstrings * Added file write encoding * Support for const class attrs * Regenerated tests * fixed bad revert * Modified file read encoding * Removed unused refs * Fixed null reference bug for composite modeler * Better default handling * Added file read utf-8 encoding * removing test for now * removed from coverage --- .../AutoRest.Core/Utilities/FileSystem.cs | 4 +- .../models/flattened_product.py | 25 ++++-- .../models/resource.py | 29 +++--- .../models/product.py | 32 ++++--- .../models/resource.py | 29 +++--- .../models/sub_product.py | 22 +++-- .../models/sub_resource.py | 15 +++- .../models/resource.py | 26 +++--- .../models/storage_account.py | 22 +++-- .../storage_account_create_parameters.py | 22 +++-- .../storage_account_update_parameters.py | 22 +++-- .../AzureServiceClientTemplateModel.cs | 2 +- .../AcceptanceTests/complex_tests.py | 13 +++ .../AcceptanceTests/file_tests.py | 6 +- .../AcceptanceTests/validation_tests.py | 12 ++- .../Python.Tests/AcceptanceTests/zzz_tests.py | 7 +- .../models/readonly_obj.py | 15 +++- ...t_parameterized_custom_host_test_client.py | 8 +- .../models/flattened_product.py | 32 ++++--- .../models/resource.py | 29 +++--- .../models/simple_product.py | 10 ++- .../models/child_product.py | 10 ++- .../models/constant_product.py | 17 ++-- .../autorestvalidationtest/models/product.py | 25 ++++-- .../TemplateModels/MethodTemplateModel.cs | 9 +- .../TemplateModels/ModelTemplateModel.cs | 90 ++++++++++++++++--- .../Python/Templates/ModelTemplate.cshtml | 66 ++++++++++---- .../Python/msrest/msrest/serialization.py | 26 +++--- .../models/resource.py | 29 +++--- .../models/storage_account.py | 25 ++++-- .../Python/swaggerpetstore/models/order.py | 15 +++- 31 files changed, 481 insertions(+), 213 deletions(-) diff --git a/AutoRest/AutoRest.Core/Utilities/FileSystem.cs b/AutoRest/AutoRest.Core/Utilities/FileSystem.cs index 231b6521090d3..13fab28c4cd4c 100644 --- a/AutoRest/AutoRest.Core/Utilities/FileSystem.cs +++ b/AutoRest/AutoRest.Core/Utilities/FileSystem.cs @@ -3,6 +3,7 @@ using System.IO; using System.Net; +using System.Text; namespace Microsoft.Rest.Generator.Utilities { @@ -10,7 +11,7 @@ public class FileSystem : IFileSystem { public void WriteFile(string path, string contents) { - File.WriteAllText(path, contents); + File.WriteAllText(path, contents, Encoding.UTF8); } public string ReadFileAsText(string path) @@ -18,6 +19,7 @@ public string ReadFileAsText(string path) using (var client = new WebClient()) { client.Headers.Add("User-Agent: AutoRest"); + client.Encoding = Encoding.UTF8; return client.DownloadString(path); } } diff --git a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/AzureResource/autorestresourceflatteningtestservice/models/flattened_product.py b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/AzureResource/autorestresourceflatteningtestservice/models/flattened_product.py index 5b5b7ea2e4c04..37bf31568e6ae 100644 --- a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/AzureResource/autorestresourceflatteningtestservice/models/flattened_product.py +++ b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/AzureResource/autorestresourceflatteningtestservice/models/flattened_product.py @@ -15,16 +15,19 @@ class FlattenedProduct(Resource): """FlattenedProduct - :param id: Resource Id - :type id: str - :param type: Resource Type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar type: Resource Type + :vartype type: str :param tags: :type tags: dict :param location: Resource Location :type location: str - :param name: Resource Name - :type name: str + :ivar name: Resource Name + :vartype name: str :param pname: :type pname: str :param lsize: @@ -33,6 +36,12 @@ class FlattenedProduct(Resource): :type provisioning_state: str """ + _validation = { + 'id': {'readonly': True}, + 'type': {'readonly': True}, + 'name': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'type': {'key': 'type', 'type': 'str'}, @@ -44,8 +53,8 @@ class FlattenedProduct(Resource): 'provisioning_state': {'key': 'properties.provisioningState', 'type': 'str'}, } - def __init__(self, id=None, type=None, tags=None, location=None, name=None, pname=None, lsize=None, provisioning_state=None): - super(FlattenedProduct, self).__init__(id=id, type=type, tags=tags, location=location, name=name) + def __init__(self, tags=None, location=None, pname=None, lsize=None, provisioning_state=None): + super(FlattenedProduct, self).__init__(tags=tags, location=location) self.pname = pname self.lsize = lsize self.provisioning_state = provisioning_state diff --git a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/AzureResource/autorestresourceflatteningtestservice/models/resource.py b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/AzureResource/autorestresourceflatteningtestservice/models/resource.py index ccc46d79f60ce..349e6b7fbd717 100644 --- a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/AzureResource/autorestresourceflatteningtestservice/models/resource.py +++ b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/AzureResource/autorestresourceflatteningtestservice/models/resource.py @@ -15,18 +15,27 @@ class Resource(Model): """Resource - :param id: Resource Id - :type id: str - :param type: Resource Type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar type: Resource Type + :vartype type: str :param tags: :type tags: dict :param location: Resource Location :type location: str - :param name: Resource Name - :type name: str + :ivar name: Resource Name + :vartype name: str """ + _validation = { + 'id': {'readonly': True}, + 'type': {'readonly': True}, + 'name': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'type': {'key': 'type', 'type': 'str'}, @@ -35,9 +44,9 @@ class Resource(Model): 'name': {'key': 'name', 'type': 'str'}, } - def __init__(self, id=None, type=None, tags=None, location=None, name=None): - self.id = id - self.type = type + def __init__(self, tags=None, location=None): + self.id = None + self.type = None self.tags = tags self.location = location - self.name = name + self.name = None diff --git a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/product.py b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/product.py index 6f322f37b09fc..1671df6f6c655 100644 --- a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/product.py +++ b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/product.py @@ -15,24 +15,34 @@ class Product(Resource): """Product - :param id: Resource Id - :type id: str - :param type: Resource Type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar type: Resource Type + :vartype type: str :param tags: :type tags: dict :param location: Resource Location :type location: str - :param name: Resource Name - :type name: str + :ivar name: Resource Name + :vartype name: str :param provisioning_state: :type provisioning_state: str - :param provisioning_state_values: Possible values include: 'Succeeded', + :ivar provisioning_state_values: Possible values include: 'Succeeded', 'Failed', 'canceled', 'Accepted', 'Creating', 'Created', 'Updating', 'Updated', 'Deleting', 'Deleted', 'OK' - :type provisioning_state_values: str + :vartype provisioning_state_values: str """ + _validation = { + 'id': {'readonly': True}, + 'type': {'readonly': True}, + 'name': {'readonly': True}, + 'provisioning_state_values': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'type': {'key': 'type', 'type': 'str'}, @@ -43,7 +53,7 @@ class Product(Resource): 'provisioning_state_values': {'key': 'properties.provisioningStateValues', 'type': 'str'}, } - def __init__(self, id=None, type=None, tags=None, location=None, name=None, provisioning_state=None, provisioning_state_values=None): - super(Product, self).__init__(id=id, type=type, tags=tags, location=location, name=name) + def __init__(self, tags=None, location=None, provisioning_state=None): + super(Product, self).__init__(tags=tags, location=location) self.provisioning_state = provisioning_state - self.provisioning_state_values = provisioning_state_values + self.provisioning_state_values = None diff --git a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/resource.py b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/resource.py index ccc46d79f60ce..349e6b7fbd717 100644 --- a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/resource.py +++ b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/resource.py @@ -15,18 +15,27 @@ class Resource(Model): """Resource - :param id: Resource Id - :type id: str - :param type: Resource Type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar type: Resource Type + :vartype type: str :param tags: :type tags: dict :param location: Resource Location :type location: str - :param name: Resource Name - :type name: str + :ivar name: Resource Name + :vartype name: str """ + _validation = { + 'id': {'readonly': True}, + 'type': {'readonly': True}, + 'name': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'type': {'key': 'type', 'type': 'str'}, @@ -35,9 +44,9 @@ class Resource(Model): 'name': {'key': 'name', 'type': 'str'}, } - def __init__(self, id=None, type=None, tags=None, location=None, name=None): - self.id = id - self.type = type + def __init__(self, tags=None, location=None): + self.id = None + self.type = None self.tags = tags self.location = location - self.name = name + self.name = None diff --git a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/sub_product.py b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/sub_product.py index 099f79ecdc9de..c813566223b8d 100644 --- a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/sub_product.py +++ b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/sub_product.py @@ -15,23 +15,31 @@ class SubProduct(SubResource): """SubProduct - :param id: Sub Resource Id - :type id: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Sub Resource Id + :vartype id: str :param provisioning_state: :type provisioning_state: str - :param provisioning_state_values: Possible values include: 'Succeeded', + :ivar provisioning_state_values: Possible values include: 'Succeeded', 'Failed', 'canceled', 'Accepted', 'Creating', 'Created', 'Updating', 'Updated', 'Deleting', 'Deleted', 'OK' - :type provisioning_state_values: str + :vartype provisioning_state_values: str """ + _validation = { + 'id': {'readonly': True}, + 'provisioning_state_values': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'provisioning_state': {'key': 'properties.provisioningState', 'type': 'str'}, 'provisioning_state_values': {'key': 'properties.provisioningStateValues', 'type': 'str'}, } - def __init__(self, id=None, provisioning_state=None, provisioning_state_values=None): - super(SubProduct, self).__init__(id=id) + def __init__(self, provisioning_state=None): + super(SubProduct, self).__init__() self.provisioning_state = provisioning_state - self.provisioning_state_values = provisioning_state_values + self.provisioning_state_values = None diff --git a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/sub_resource.py b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/sub_resource.py index 46ce23db785ac..77ec6cf29f437 100644 --- a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/sub_resource.py +++ b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/Lro/autorestlongrunningoperationtestservice/models/sub_resource.py @@ -15,13 +15,20 @@ class SubResource(Model): """SubResource - :param id: Sub Resource Id - :type id: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Sub Resource Id + :vartype id: str """ + _validation = { + 'id': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, } - def __init__(self, id=None): - self.id = id + def __init__(self): + self.id = None diff --git a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/resource.py b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/resource.py index 9025ae9819072..8d25c5c02f7ac 100644 --- a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/resource.py +++ b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/resource.py @@ -15,12 +15,15 @@ class Resource(Model): """Resource - :param id: Resource Id - :type id: str - :param name: Resource name - :type name: str - :param type: Resource type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar name: Resource name + :vartype name: str + :ivar type: Resource type + :vartype type: str :param location: Resource location :type location: str :param tags: Resource tags @@ -28,6 +31,9 @@ class Resource(Model): """ _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, 'location': {'required': True}, } @@ -39,9 +45,9 @@ class Resource(Model): 'tags': {'key': 'tags', 'type': '{str}'}, } - def __init__(self, location, id=None, name=None, type=None, tags=None): - self.id = id - self.name = name - self.type = type + def __init__(self, location, tags=None): + self.id = None + self.name = None + self.type = None self.location = location self.tags = tags diff --git a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account.py b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account.py index 3268320474a92..bff1707d78076 100644 --- a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account.py +++ b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account.py @@ -16,12 +16,15 @@ class StorageAccount(Resource): """ The storage account. - :param id: Resource Id - :type id: str - :param name: Resource name - :type name: str - :param type: Resource type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar name: Resource name + :vartype name: str + :ivar type: Resource type + :vartype type: str :param location: Resource location :type location: str :param tags: Resource tags @@ -77,6 +80,9 @@ class StorageAccount(Resource): """ _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, 'location': {'required': True}, } @@ -99,8 +105,8 @@ class StorageAccount(Resource): 'secondary_endpoints': {'key': 'properties.secondaryEndpoints', 'type': 'Endpoints'}, } - def __init__(self, location, id=None, name=None, type=None, tags=None, provisioning_state=None, account_type=None, primary_endpoints=None, primary_location=None, status_of_primary=None, last_geo_failover_time=None, secondary_location=None, status_of_secondary=None, creation_time=None, custom_domain=None, secondary_endpoints=None): - super(StorageAccount, self).__init__(id=id, name=name, type=type, location=location, tags=tags) + def __init__(self, location, tags=None, provisioning_state=None, account_type=None, primary_endpoints=None, primary_location=None, status_of_primary=None, last_geo_failover_time=None, secondary_location=None, status_of_secondary=None, creation_time=None, custom_domain=None, secondary_endpoints=None): + super(StorageAccount, self).__init__(location=location, tags=tags) self.provisioning_state = provisioning_state self.account_type = account_type self.primary_endpoints = primary_endpoints diff --git a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account_create_parameters.py b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account_create_parameters.py index 449f6c502135a..31535a2dc8c20 100644 --- a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account_create_parameters.py +++ b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account_create_parameters.py @@ -16,12 +16,15 @@ class StorageAccountCreateParameters(Resource): """ The parameters to provide for the account. - :param id: Resource Id - :type id: str - :param name: Resource name - :type name: str - :param type: Resource type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar name: Resource name + :vartype name: str + :ivar type: Resource type + :vartype type: str :param location: Resource location :type location: str :param tags: Resource tags @@ -33,6 +36,9 @@ class StorageAccountCreateParameters(Resource): """ _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, 'location': {'required': True}, } @@ -45,6 +51,6 @@ class StorageAccountCreateParameters(Resource): 'account_type': {'key': 'properties.accountType', 'type': 'AccountType'}, } - def __init__(self, location, id=None, name=None, type=None, tags=None, account_type=None): - super(StorageAccountCreateParameters, self).__init__(id=id, name=name, type=type, location=location, tags=tags) + def __init__(self, location, tags=None, account_type=None): + super(StorageAccountCreateParameters, self).__init__(location=location, tags=tags) self.account_type = account_type diff --git a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account_update_parameters.py b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account_update_parameters.py index 5975e56879436..e60b187d613bd 100644 --- a/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account_update_parameters.py +++ b/AutoRest/Generators/Python/Azure.Python.Tests/Expected/AcceptanceTests/StorageManagementClient/storagemanagementclient/models/storage_account_update_parameters.py @@ -16,12 +16,15 @@ class StorageAccountUpdateParameters(Resource): """ The parameters to update on the account. - :param id: Resource Id - :type id: str - :param name: Resource name - :type name: str - :param type: Resource type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar name: Resource name + :vartype name: str + :ivar type: Resource type + :vartype type: str :param location: Resource location :type location: str :param tags: Resource tags @@ -41,6 +44,9 @@ class StorageAccountUpdateParameters(Resource): """ _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, 'location': {'required': True}, } @@ -54,7 +60,7 @@ class StorageAccountUpdateParameters(Resource): 'custom_domain': {'key': 'properties.customDomain', 'type': 'CustomDomain'}, } - def __init__(self, location, id=None, name=None, type=None, tags=None, account_type=None, custom_domain=None): - super(StorageAccountUpdateParameters, self).__init__(id=id, name=name, type=type, location=location, tags=tags) + def __init__(self, location, tags=None, account_type=None, custom_domain=None): + super(StorageAccountUpdateParameters, self).__init__(location=location, tags=tags) self.account_type = account_type self.custom_domain = custom_domain diff --git a/AutoRest/Generators/Python/Azure.Python/TemplateModels/AzureServiceClientTemplateModel.cs b/AutoRest/Generators/Python/Azure.Python/TemplateModels/AzureServiceClientTemplateModel.cs index 3c0cfcbf8160c..f56bc3935547a 100644 --- a/AutoRest/Generators/Python/Azure.Python/TemplateModels/AzureServiceClientTemplateModel.cs +++ b/AutoRest/Generators/Python/Azure.Python/TemplateModels/AzureServiceClientTemplateModel.cs @@ -74,7 +74,7 @@ public override string RequiredConstructorParameters else { string defaultValue = PythonConstants.None; - if (property.DefaultValue != null && property.Type is PrimaryType) + if (!string.IsNullOrWhiteSpace(property.DefaultValue) && property.Type is PrimaryType) { defaultValue = property.DefaultValue; } diff --git a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/complex_tests.py b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/complex_tests.py index fcbf2f38617cf..fa4902659e391 100644 --- a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/complex_tests.py +++ b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/complex_tests.py @@ -201,6 +201,19 @@ def test_complex(self): # PUT primitive/byte client.primitive.put_byte(valid_bytes) + """ + COMPLEX TYPE WITH READ ONLY PROPERTIES + """ + # GET readonly/valid + valid_obj = ReadonlyObj(size=2) + valid_obj.id = '1234' + readonly_result = client.readonlyproperty.get_valid() + self.assertEqual(readonly_result, valid_obj) + + # PUT readonly/valid + readonly_result = client.readonlyproperty.put_valid(valid_obj) + self.assertIsNone(readonly_result) + """ COMPLEX TYPE WITH ARRAY PROPERTIES """ diff --git a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/file_tests.py b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/file_tests.py index c45c3e0db6836..900265a4ed1fe 100644 --- a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/file_tests.py +++ b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/file_tests.py @@ -100,10 +100,10 @@ def add_headers(adapter, request, response, *args, **kwargs): file_length = 0 client._client.add_hook('response', add_headers) stream = client.files.get_file_large(callback=test_callback) - for data in stream: - file_length += len(data) + #for data in stream: + # file_length += len(data) - self.assertEqual(file_length, 3000 * 1024 * 1024) + #self.assertEqual(file_length, 3000 * 1024 * 1024) def test_files_raw(self): diff --git a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/validation_tests.py b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/validation_tests.py index 5af07d6773dbd..7e1ba54e0556e 100644 --- a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/validation_tests.py +++ b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/validation_tests.py @@ -67,10 +67,7 @@ def test_constant_values(self): client.get_with_constant_in_path() - # TODO: Const body should be built implicitly body = Product(child=ChildProduct()) - body.const_child = ConstantProduct() - product = client.post_with_constant_in_body(body=body) self.assertIsNotNone(product) @@ -121,7 +118,6 @@ def test_validation(self): try: tempproduct=Product(child=ChildProduct(), capacity=0) - tempproduct.const_child=ConstantProduct() client.validation_of_body("123", 150, tempproduct) except ValidationError as err: self.assertEqual(err.rule, "minimum_ex") @@ -129,7 +125,6 @@ def test_validation(self): try: tempproduct=Product(child=ChildProduct(), capacity=100) - tempproduct.const_child=ConstantProduct() client.validation_of_body("123", 150, tempproduct) except ValidationError as err: self.assertEqual(err.rule, "maximum_ex") @@ -138,7 +133,6 @@ def test_validation(self): try: tempproduct=Product(child=ChildProduct(), display_names=["item1","item2","item3","item4","item5","item6","item7"]) - tempproduct.const_child=ConstantProduct() client.validation_of_body("123", 150, tempproduct) except ValidationError as err: self.assertEqual(err.rule, "max_items") @@ -155,4 +149,8 @@ def test_validation(self): client2.validation_of_method_parameters("123", 150) except ValidationError as err: self.assertEqual(err.rule, "pattern") - self.assertEqual(err.target, "self.config.api_version") \ No newline at end of file + self.assertEqual(err.target, "self.config.api_version") + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/zzz_tests.py b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/zzz_tests.py index ccafa013129b1..2afd6aa9c3c70 100644 --- a/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/zzz_tests.py +++ b/AutoRest/Generators/Python/Python.Tests/AcceptanceTests/zzz_tests.py @@ -54,6 +54,7 @@ def test_ensure_coverage(self): client = AutoRestReportService(config) report = client.get_report() + # Add tests that wont be supported due to the nature of Python here not_supported = { 'getIntegerOverflow': 1, 'getIntegerUnderflow': 1, @@ -64,10 +65,8 @@ def test_ensure_coverage(self): 'HttpRedirect300Get': 1, } - # TODO: Support ignore readonly property in http put - missing_features_or_bugs = { - 'putComplexReadOnlyPropertyValid': 1, - } + # Please add missing features or failing tests here + missing_features_or_bugs = {'FileStreamVeryLarge' : 1} report.update(not_supported) report.update(missing_features_or_bugs) diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyComplex/autorestcomplextestservice/models/readonly_obj.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyComplex/autorestcomplextestservice/models/readonly_obj.py index f0db42cc1b3f9..109d15efbe7e4 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyComplex/autorestcomplextestservice/models/readonly_obj.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/BodyComplex/autorestcomplextestservice/models/readonly_obj.py @@ -15,17 +15,24 @@ class ReadonlyObj(Model): """ReadonlyObj - :param id: - :type id: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: + :vartype id: str :param size: :type size: int """ + _validation = { + 'id': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'size': {'key': 'size', 'type': 'int'}, } - def __init__(self, id=None, size=None): - self.id = id + def __init__(self, size=None): + self.id = None self.size = size diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/CustomBaseUriMoreOptions/autorestparameterizedcustomhosttestclient/auto_rest_parameterized_custom_host_test_client.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/CustomBaseUriMoreOptions/autorestparameterizedcustomhosttestclient/auto_rest_parameterized_custom_host_test_client.py index 1e9140c3e5f70..6a61d697f46ef 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/CustomBaseUriMoreOptions/autorestparameterizedcustomhosttestclient/auto_rest_parameterized_custom_host_test_client.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/CustomBaseUriMoreOptions/autorestparameterizedcustomhosttestclient/auto_rest_parameterized_custom_host_test_client.py @@ -33,9 +33,13 @@ def __init__( self, subscription_id, dns_suffix, filepath=None): if subscription_id is None: - raise ValueError('subscription_id must not be None.') + raise ValueError("Parameter 'subscription_id' must not be None.") + if not isinstance(subscription_id, str): + raise TypeError("Parameter 'subscription_id' must be str.") if dns_suffix is None: - raise ValueError('dns_suffix must not be None.') + raise ValueError("Parameter 'dns_suffix' must not be None.") + if not isinstance(dns_suffix, str): + raise TypeError("Parameter 'dns_suffix' must be str.") base_url = '{vault}{secret}{dnsSuffix}' super(AutoRestParameterizedCustomHostTestClientConfiguration, self).__init__(base_url, filepath) diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/flattened_product.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/flattened_product.py index 99e49b9aa384f..55df7eedae364 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/flattened_product.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/flattened_product.py @@ -15,28 +15,38 @@ class FlattenedProduct(Resource): """FlattenedProduct - :param id: Resource Id - :type id: str - :param type: Resource Type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar type: Resource Type + :vartype type: str :param tags: :type tags: dict :param location: Resource Location :type location: str - :param name: Resource Name - :type name: str + :ivar name: Resource Name + :vartype name: str :param pname: :type pname: str :param flattened_product_type: :type flattened_product_type: str - :param provisioning_state_values: Possible values include: 'Succeeded', + :ivar provisioning_state_values: Possible values include: 'Succeeded', 'Failed', 'canceled', 'Accepted', 'Creating', 'Created', 'Updating', 'Updated', 'Deleting', 'Deleted', 'OK' - :type provisioning_state_values: str + :vartype provisioning_state_values: str :param provisioning_state: :type provisioning_state: str """ + _validation = { + 'id': {'readonly': True}, + 'type': {'readonly': True}, + 'name': {'readonly': True}, + 'provisioning_state_values': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'type': {'key': 'type', 'type': 'str'}, @@ -49,9 +59,9 @@ class FlattenedProduct(Resource): 'provisioning_state': {'key': 'properties.provisioningState', 'type': 'str'}, } - def __init__(self, id=None, type=None, tags=None, location=None, name=None, pname=None, flattened_product_type=None, provisioning_state_values=None, provisioning_state=None): - super(FlattenedProduct, self).__init__(id=id, type=type, tags=tags, location=location, name=name) + def __init__(self, tags=None, location=None, pname=None, flattened_product_type=None, provisioning_state=None): + super(FlattenedProduct, self).__init__(tags=tags, location=location) self.pname = pname self.flattened_product_type = flattened_product_type - self.provisioning_state_values = provisioning_state_values + self.provisioning_state_values = None self.provisioning_state = provisioning_state diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/resource.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/resource.py index ccc46d79f60ce..349e6b7fbd717 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/resource.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/resource.py @@ -15,18 +15,27 @@ class Resource(Model): """Resource - :param id: Resource Id - :type id: str - :param type: Resource Type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar type: Resource Type + :vartype type: str :param tags: :type tags: dict :param location: Resource Location :type location: str - :param name: Resource Name - :type name: str + :ivar name: Resource Name + :vartype name: str """ + _validation = { + 'id': {'readonly': True}, + 'type': {'readonly': True}, + 'name': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'type': {'key': 'type', 'type': 'str'}, @@ -35,9 +44,9 @@ class Resource(Model): 'name': {'key': 'name', 'type': 'str'}, } - def __init__(self, id=None, type=None, tags=None, location=None, name=None): - self.id = id - self.type = type + def __init__(self, tags=None, location=None): + self.id = None + self.type = None self.tags = tags self.location = location - self.name = name + self.name = None diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/simple_product.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/simple_product.py index 1be642054ecac..02537d1797c79 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/simple_product.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/ModelFlattening/autorestresourceflatteningtestservice/models/simple_product.py @@ -16,6 +16,9 @@ class SimpleProduct(BaseProduct): """ The product documentation. + Variables are only populated by the server, and will be ignored when + sending a request. + :param product_id: Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles. @@ -24,9 +27,9 @@ class SimpleProduct(BaseProduct): :type description: str :param max_product_display_name: Display name of product. :type max_product_display_name: str - :param capacity: Capacity of product. For example, 4 people. Default + :ivar capacity: Capacity of product. For example, 4 people. Default value: "Large" . - :type capacity: str + :vartype capacity: str :param generic_value: Generic URL value. :type generic_value: str :param odatavalue: URL value. @@ -48,9 +51,10 @@ class SimpleProduct(BaseProduct): 'odatavalue': {'key': 'details.max_product_image.@odata\\.value', 'type': 'str'}, } + capacity = "Large" + def __init__(self, product_id, max_product_display_name, description=None, generic_value=None, odatavalue=None): super(SimpleProduct, self).__init__(product_id=product_id, description=description) self.max_product_display_name = max_product_display_name - self.capacity = "Large" self.generic_value = generic_value self.odatavalue = odatavalue diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/child_product.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/child_product.py index e7270ddf47641..0ef944dd8e9ca 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/child_product.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/child_product.py @@ -16,8 +16,11 @@ class ChildProduct(Model): """ The product documentation. - :param const_property: Constant string. Default value: "constant" . - :type const_property: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar const_property: Constant string. Default value: "constant" . + :vartype const_property: str :param count: Count :type count: int """ @@ -31,6 +34,7 @@ class ChildProduct(Model): 'count': {'key': 'count', 'type': 'int'}, } + const_property = "constant" + def __init__(self, count=None): - self.const_property = "constant" self.count = count diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/constant_product.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/constant_product.py index 14f416cef6f00..cb4531c4ec1aa 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/constant_product.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/constant_product.py @@ -16,10 +16,13 @@ class ConstantProduct(Model): """ The product documentation. - :param const_property: Constant string. Default value: "constant" . - :type const_property: str - :param const_property2: Constant string2. Default value: "constant2" . - :type const_property2: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar const_property: Constant string. Default value: "constant" . + :vartype const_property: str + :ivar const_property2: Constant string2. Default value: "constant2" . + :vartype const_property2: str """ _validation = { @@ -32,6 +35,6 @@ class ConstantProduct(Model): 'const_property2': {'key': 'constProperty2', 'type': 'str'}, } - def __init__(self): - self.const_property = "constant" - self.const_property2 = "constant2" + const_property = "constant" + + const_property2 = "constant2" diff --git a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/product.py b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/product.py index c9577fa07f23d..65d49c5d12e17 100644 --- a/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/product.py +++ b/AutoRest/Generators/Python/Python.Tests/Expected/AcceptanceTests/Validation/autorestvalidationtest/models/product.py @@ -9,6 +9,7 @@ # regenerated. # -------------------------------------------------------------------------- +from .constant_product import ConstantProduct from msrest.serialization import Model @@ -16,6 +17,9 @@ class Product(Model): """ The product documentation. + Variables are only populated by the server, and will be ignored when + sending a request. + :param display_names: Non required array of unique items from 0 to 6 elements. :type display_names: list of str @@ -26,13 +30,13 @@ class Product(Model): :param child: :type child: :class:`ChildProduct ` - :param const_child: - :type const_child: :class:`ConstantProduct + :ivar const_child: + :vartype const_child: :class:`ConstantProduct ` - :param const_int: Constant int. Default value: 0 . - :type const_int: int - :param const_string: Constant string. Default value: "constant" . - :type const_string: str + :ivar const_int: Constant int. Default value: 0 . + :vartype const_int: int + :ivar const_string: Constant string. Default value: "constant" . + :vartype const_string: str :param const_string_as_enum: Constant string as Enum. Possible values include: 'constant_string_as_enum' :type const_string_as_enum: str @@ -59,12 +63,15 @@ class Product(Model): 'const_string_as_enum': {'key': 'constStringAsEnum', 'type': 'EnumConst'}, } + const_child = ConstantProduct() + + const_int = 0 + + const_string = "constant" + def __init__(self, child, display_names=None, capacity=None, image=None, const_string_as_enum=None): self.display_names = display_names self.capacity = capacity self.image = image self.child = child - self.const_child = None - self.const_int = 0 - self.const_string = "constant" self.const_string_as_enum = const_string_as_enum diff --git a/AutoRest/Generators/Python/Python/TemplateModels/MethodTemplateModel.cs b/AutoRest/Generators/Python/Python/TemplateModels/MethodTemplateModel.cs index 0555d5cd4434e..ee71dc5b08b4d 100644 --- a/AutoRest/Generators/Python/Python/TemplateModels/MethodTemplateModel.cs +++ b/AutoRest/Generators/Python/Python/TemplateModels/MethodTemplateModel.cs @@ -33,6 +33,13 @@ public MethodTemplateModel(Method source, ServiceClient serviceClient) } AddCustomHeader = true; string formatter; + foreach (var parameter in LocalParameters) + { + if (string.IsNullOrWhiteSpace(parameter.DefaultValue)) + { + parameter.DefaultValue = PythonConstants.None; + } + } foreach (Match m in Regex.Matches(Url, @"\{[\w]+:[\w]+\}")) { formatter = m.Value.Split(':').First() + '}'; @@ -158,7 +165,7 @@ public virtual string MethodParameterDeclaration(bool addCustomHeaderParameters) foreach (var parameter in LocalParameters) { - if (parameter.IsRequired && parameter.DefaultValue.Equals(PythonConstants.None)) + if (parameter.IsRequired && parameter.DefaultValue == PythonConstants.None) { requiredDeclarations.Add(parameter.Name); } diff --git a/AutoRest/Generators/Python/Python/TemplateModels/ModelTemplateModel.cs b/AutoRest/Generators/Python/Python/TemplateModels/ModelTemplateModel.cs index dbd8fd040ff5f..2c43e7c092ca7 100644 --- a/AutoRest/Generators/Python/Python/TemplateModels/ModelTemplateModel.cs +++ b/AutoRest/Generators/Python/Python/TemplateModels/ModelTemplateModel.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Text; using Microsoft.Rest.Generator.ClientModel; using Microsoft.Rest.Generator.Python.TemplateModels; using Microsoft.Rest.Generator.Utilities; @@ -48,6 +49,14 @@ public ModelTemplateModel(CompositeType source, ServiceClient serviceClient) _parent = new ModelTemplateModel(source.BaseModelType, serviceClient); } + foreach (var property in ComposedProperties) + { + if (string.IsNullOrWhiteSpace(property.DefaultValue)) + { + property.DefaultValue = PythonConstants.None; + } + } + if (this.IsPolymorphic) { foreach (var modelType in ServiceClient.ModelTypes) @@ -104,6 +113,10 @@ public IList Validators { validation.Add("'constant': True"); } + if (parameter.IsReadOnly) + { + validation.Add("'readonly': True"); + } if (parameter.Constraints.Any()) { validation.AddRange(BuildValidationParameters(parameter.Constraints)); @@ -190,6 +203,22 @@ public bool HasParent get { return this._parent != null; } } + public bool NeedsConstructor + { + get + { + var nonConstant = Properties.Where(p => !p.IsConstant); + if (nonConstant.Any()) + { + return true; + } + else + { + return (HasParent || NeedsPolymorphicConverter); + } + } + } + /// /// Provides the modelProperty documentation string along with default value if any. /// @@ -202,11 +231,14 @@ public static string GetPropertyDocumentationString(Property property) { throw new ArgumentNullException("property"); } - string docString = string.Format(CultureInfo.InvariantCulture, ":param {0}:", property.Name); - + if (property.IsConstant || property.IsReadOnly) + { + docString = string.Format(CultureInfo.InvariantCulture, ":ivar {0}:", property.Name); + } + string documentation = property.Documentation; - if (!string.IsNullOrWhiteSpace(property.DefaultValue) && property.DefaultValue != PythonConstants.None) + if (property.DefaultValue != PythonConstants.None) { if (documentation != null && !documentation.EndsWith(".", StringComparison.OrdinalIgnoreCase)) { @@ -243,16 +275,43 @@ public IList RequiredFieldsList } } + public IEnumerable ReadOnlyAttributes + { + get + { + return ComposedProperties.Where(p => p.IsConstant || p.IsReadOnly); + } + } + + public IDictionary ComplexConstants + { + get + { + Dictionary complexConstant = new Dictionary (); + foreach (var property in Properties) + { + if (property.IsConstant) + { + CompositeType compType = property.Type as CompositeType; + if (compType != null) + { + complexConstant[property.Name] = compType; + } + } + } + return complexConstant; + } + } + public virtual string SuperParameterDeclaration() { List combinedDeclarations = new List(); - foreach (var property in ComposedProperties.Except(Properties)) + foreach (var property in ComposedProperties.Except(Properties).Except(ReadOnlyAttributes)) { if (this.IsPolymorphic) if (property.Name == this.BasePolymorphicDiscriminator) continue; - combinedDeclarations.Add(string.Format(CultureInfo.InvariantCulture, "{0}={0}", property.Name)); } return string.Join(", ", combinedDeclarations); @@ -264,17 +323,13 @@ public virtual string MethodParameterDeclaration() List requiredDeclarations = new List(); List combinedDeclarations = new List(); - foreach (var property in ComposedProperties) + foreach (var property in ComposedProperties.Except(ReadOnlyAttributes)) { if (this.IsPolymorphic) if (property.Name == this.BasePolymorphicDiscriminator) continue; - if (property.IsConstant) - { - continue; - } - if (property.IsRequired && property.DefaultValue.Equals(PythonConstants.None)) + if (property.IsRequired && property.DefaultValue == PythonConstants.None) { requiredDeclarations.Add(property.Name); } @@ -363,9 +418,20 @@ public string InitializeProperty(string objectName, Property property) { throw new ArgumentNullException("property"); } + if (property.IsReadOnly) + { + return string.Format(CultureInfo.InvariantCulture, "{0}.{1} = None", objectName, property.Name); + } if (property.IsConstant) { - return string.Format(CultureInfo.InvariantCulture, "{0}.{1} = {2}", objectName, property.Name, property.DefaultValue); + if (ComplexConstants.ContainsKey(property.Name)) + { + return string.Format(CultureInfo.InvariantCulture, "{0} = {1}()", property.Name, property.Type.Name); + } + else + { + return string.Format(CultureInfo.InvariantCulture, "{0} = {1}", property.Name, property.DefaultValue); + } } if (IsPolymorphic) { diff --git a/AutoRest/Generators/Python/Python/Templates/ModelTemplate.cshtml b/AutoRest/Generators/Python/Python/Templates/ModelTemplate.cshtml index 40ee94f75cb4b..17e86079f9cfd 100644 --- a/AutoRest/Generators/Python/Python/Templates/ModelTemplate.cshtml +++ b/AutoRest/Generators/Python/Python/Templates/ModelTemplate.cshtml @@ -11,6 +11,13 @@ @Header("# ").TrimMultilineHeader() # -------------------------------------------------------------------------- @EmptyLine +@if(Model.ComplexConstants.Any()) +{ + foreach(var property in Model.ComplexConstants.Values) + { +@:from .@property.Name.ToPythonCase() import @property.Name + } +} @if (Model.BaseModelType != null) { @:from .@Model.BaseModelType.Name.ToPythonCase() import @Model.BaseModelType.Name @@ -42,14 +49,26 @@ else { @: """@(Model.Name) } +@if (Model.ReadOnlyAttributes.Any()) +{ +@EmptyLine +@: @WrapComment(string.Empty, "Variables are only populated by the server, and will be ignored when sending a request.") +} @if (Model.ComposedProperties.Any()) { @EmptyLine - foreach (var property in Model.ComposedProperties) - { + foreach (var property in Model.ComposedProperties) + { @: @ParameterWrapComment(string.Empty, ModelTemplateModel.GetPropertyDocumentationString(property)) + if (property.IsConstant || property.IsReadOnly) + { +@: @ParameterWrapComment(string.Empty, ":vartype " + property.Name + ": " + Model.GetPropertyDocumentationType(property.Type)) + } + else + { @: @ParameterWrapComment(string.Empty, ":type " + property.Name + ": " + Model.GetPropertyDocumentationType(property.Type)) - } + } + } } """ @if (Model.Validators.Any() || Model.RequiredFieldsList.Any()) @@ -89,26 +108,37 @@ else } } -@EmptyLine - def __init__(self@(Model.MethodParameterDeclaration())): -@if (Model.HasParent) +@foreach(var property in Model.Properties) { - @:super(@(Model.Name), self).__init__(@(Model.SuperParameterDeclaration())) + if (property.IsConstant) + { +@EmptyLine + @:@(Model.InitializeProperty(String.Empty, property)) + } } -@{ - var propertyList = new List(Model.Properties); - if (propertyList.Count > 0) - { - for (int i = 0; i < propertyList.Count; i++) - { +@if (Model.NeedsConstructor) +{ +@EmptyLine + @:def __init__(self@(Model.MethodParameterDeclaration())): + if (Model.HasParent) + { + @:super(@(Model.Name), self).__init__(@(Model.SuperParameterDeclaration())) + } + var propertyList = new List(Model.Properties); + if (propertyList.Count > 0) + { + for (int i = 0; i < propertyList.Count; i++) + { + if (!propertyList[i].IsConstant) + { @:@(Model.InitializeProperty("self", propertyList[i])) - } + } + } } -} - -@if (Model.NeedsPolymorphicConverter) -{ + if (Model.NeedsPolymorphicConverter) + { @:self.@Model.BasePolymorphicDiscriminator = '@Model.SerializedName' + } } @if (Model.IsException) { diff --git a/ClientRuntimes/Python/msrest/msrest/serialization.py b/ClientRuntimes/Python/msrest/msrest/serialization.py index 25aff136d1538..4675cd6aacee7 100644 --- a/ClientRuntimes/Python/msrest/msrest/serialization.py +++ b/ClientRuntimes/Python/msrest/msrest/serialization.py @@ -186,7 +186,8 @@ def _serialize(self, target_obj, data_type=None, **kwargs): attr_type = map['type'] orig_attr = getattr(target_obj, attr) validation = target_obj._validation.get(attr_name, {}) - self.validate(orig_attr, attr_name, **validation) + orig_attr = self.validate( + orig_attr, attr_name, **validation) new_attr = self.serialize_data( orig_attr, attr_type, **kwargs) @@ -245,7 +246,7 @@ def url(self, name, data, data_type, **kwargs): :raises: TypeError if serialization fails. :raises: ValueError if data is None """ - self.validate(data, name, required=True, **kwargs) + data = self.validate(data, name, required=True, **kwargs) try: output = self.serialize_data(data, data_type, **kwargs) if data_type == 'bool': @@ -269,7 +270,7 @@ def query(self, name, data, data_type, **kwargs): :raises: TypeError if serialization fails. :raises: ValueError if data is None """ - self.validate(data, name, required=True, **kwargs) + data = self.validate(data, name, required=True, **kwargs) try: if data_type in ['[str]']: data = ["" if d is None else d for d in data] @@ -295,7 +296,7 @@ def header(self, name, data, data_type, **kwargs): :raises: TypeError if serialization fails. :raises: ValueError if data is None """ - self.validate(data, name, required=True, **kwargs) + data = self.validate(data, name, required=True, **kwargs) try: if data_type in ['[str]']: data = ["" if d is None else d for d in data] @@ -315,6 +316,8 @@ def validate(self, data, name, **kwargs): raise ValidationError("required", name, True) elif data is None: return + elif kwargs.get('readonly'): + return try: for key, value in kwargs.items(): @@ -323,6 +326,8 @@ def validate(self, data, name, **kwargs): raise ValidationError(key, name, value) except TypeError: raise ValidationError("unknown", name) + else: + return data def serialize_data(self, data, data_type, **kwargs): """Serialize generic data according to supplied data type. @@ -690,15 +695,14 @@ def _instantiate_model(self, response, attrs): if callable(response): subtype = response._get_subtype_map() try: - consts = [k for k, v in response._validation.items() - if v.get('constant')] + readonly = [k for k, v in response._validation.items() + if v.get('readonly')] + const = [k for k, v in response._validation.items() + if v.get('constant')] kwargs = {k: v for k, v in attrs.items() - if k not in subtype and k not in consts} + if k not in subtype and k not in readonly + const} response_obj = response(**kwargs) - - # We have to do this until we resolve the issue of complex - # constant attributes not being auto-instantiated. - for attr in consts: + for attr in readonly: setattr(response_obj, attr, attrs.get(attr)) return response_obj except TypeError as err: diff --git a/Samples/azure-storage/Azure.Python/storagemanagementclient/models/resource.py b/Samples/azure-storage/Azure.Python/storagemanagementclient/models/resource.py index e784f717664d9..87763cb25e4b4 100644 --- a/Samples/azure-storage/Azure.Python/storagemanagementclient/models/resource.py +++ b/Samples/azure-storage/Azure.Python/storagemanagementclient/models/resource.py @@ -8,18 +8,27 @@ class Resource(Model): """Resource - :param id: Resource Id - :type id: str - :param name: Resource name - :type name: str - :param type: Resource type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar name: Resource name + :vartype name: str + :ivar type: Resource type + :vartype type: str :param location: Resource location :type location: str :param tags: Resource tags :type tags: dict """ + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'name': {'key': 'name', 'type': 'str'}, @@ -28,9 +37,9 @@ class Resource(Model): 'tags': {'key': 'tags', 'type': '{str}'}, } - def __init__(self, id=None, name=None, type=None, location=None, tags=None): - self.id = id - self.name = name - self.type = type + def __init__(self, location=None, tags=None): + self.id = None + self.name = None + self.type = None self.location = location self.tags = tags diff --git a/Samples/azure-storage/Azure.Python/storagemanagementclient/models/storage_account.py b/Samples/azure-storage/Azure.Python/storagemanagementclient/models/storage_account.py index 274af1b58793d..1aeaa6bc9f22d 100644 --- a/Samples/azure-storage/Azure.Python/storagemanagementclient/models/storage_account.py +++ b/Samples/azure-storage/Azure.Python/storagemanagementclient/models/storage_account.py @@ -9,12 +9,15 @@ class StorageAccount(Resource): """ The storage account. - :param id: Resource Id - :type id: str - :param name: Resource name - :type name: str - :param type: Resource type - :type type: str + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: Resource Id + :vartype id: str + :ivar name: Resource name + :vartype name: str + :ivar type: Resource type + :vartype type: str :param location: Resource location :type location: str :param tags: Resource tags @@ -24,6 +27,12 @@ class StorageAccount(Resource): ` """ + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'str'}, 'name': {'key': 'name', 'type': 'str'}, @@ -33,6 +42,6 @@ class StorageAccount(Resource): 'properties': {'key': 'properties', 'type': 'StorageAccountProperties'}, } - def __init__(self, id=None, name=None, type=None, location=None, tags=None, properties=None): - super(StorageAccount, self).__init__(id=id, name=name, type=type, location=location, tags=tags) + def __init__(self, location=None, tags=None, properties=None): + super(StorageAccount, self).__init__(location=location, tags=tags) self.properties = properties diff --git a/Samples/petstore/Python/swaggerpetstore/models/order.py b/Samples/petstore/Python/swaggerpetstore/models/order.py index 8b7b27a2bbe96..6e1f3c0aac504 100644 --- a/Samples/petstore/Python/swaggerpetstore/models/order.py +++ b/Samples/petstore/Python/swaggerpetstore/models/order.py @@ -8,8 +8,11 @@ class Order(Model): """Order - :param id: - :type id: long + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: + :vartype id: long :param pet_id: :type pet_id: long :param quantity: @@ -23,6 +26,10 @@ class Order(Model): :type complete: bool """ + _validation = { + 'id': {'readonly': True}, + } + _attribute_map = { 'id': {'key': 'id', 'type': 'long'}, 'pet_id': {'key': 'petId', 'type': 'long'}, @@ -32,8 +39,8 @@ class Order(Model): 'complete': {'key': 'complete', 'type': 'bool'}, } - def __init__(self, id=None, pet_id=None, quantity=None, ship_date=None, status=None, complete=None): - self.id = id + def __init__(self, pet_id=None, quantity=None, ship_date=None, status=None, complete=None): + self.id = None self.pet_id = pet_id self.quantity = quantity self.ship_date = ship_date From a7b6eb94049bae8e7eaa69b066791e7ed40dc2f8 Mon Sep 17 00:00:00 2001 From: John Hart Date: Fri, 22 Apr 2016 15:01:03 -0700 Subject: [PATCH 13/16] Added an ExitCode to Autorest.exe (#966) --- AutoRest/AutoRest/AutoRest.csproj | 3 ++- AutoRest/AutoRest/ExitCode.cs | 14 ++++++++++++++ AutoRest/AutoRest/Program.cs | 6 +++++- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 AutoRest/AutoRest/ExitCode.cs diff --git a/AutoRest/AutoRest/AutoRest.csproj b/AutoRest/AutoRest/AutoRest.csproj index 8bd5ec84e97dd..7c578e0980acb 100644 --- a/AutoRest/AutoRest/AutoRest.csproj +++ b/AutoRest/AutoRest/AutoRest.csproj @@ -31,6 +31,7 @@ Properties\AssemblyVersionInfo.cs + @@ -70,4 +71,4 @@ - + \ No newline at end of file diff --git a/AutoRest/AutoRest/ExitCode.cs b/AutoRest/AutoRest/ExitCode.cs new file mode 100644 index 0000000000000..fe09a82b44c21 --- /dev/null +++ b/AutoRest/AutoRest/ExitCode.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace Microsoft.Rest.Generator.Cli +{ + /// + /// Available exit codes. + /// + public enum ExitCode : int + { + Success = 0, + Error = 1 + } +} diff --git a/AutoRest/AutoRest/Program.cs b/AutoRest/AutoRest/Program.cs index b0c4aa7abea2b..f7bdf406b1066 100644 --- a/AutoRest/AutoRest/Program.cs +++ b/AutoRest/AutoRest/Program.cs @@ -12,8 +12,10 @@ namespace Microsoft.Rest.Generator.Cli { internal class Program { - private static void Main(string[] args) + private static int Main(string[] args) { + int exitCode = (int)ExitCode.Error; + try { Settings settings = null; @@ -69,6 +71,7 @@ private static void Main(string[] args) { Console.WriteLine(Resources.GenerationComplete, settings.CodeGenerator, settings.Input); + exitCode = (int)ExitCode.Success; } } @@ -91,6 +94,7 @@ private static void Main(string[] args) Console.Error.WriteLine(Resources.ConsoleErrorMessage, exception.Message); Console.Error.WriteLine(Resources.ConsoleErrorStackTrace, exception.StackTrace); } + return exitCode; } /// From 1e0252d4e3d0ff5d67fae439010a32a9dd90fcb2 Mon Sep 17 00:00:00 2001 From: Hovsep Date: Fri, 22 Apr 2016 16:35:14 -0700 Subject: [PATCH 14/16] Revert "Escape data strings for odata queries." --- .../Azure.CSharp.Tests/AcceptanceTests.cs | 3 +-- .../ODataTests.cs | 21 +++++++------------ .../OData/ODataQuery.cs | 6 +++--- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/AutoRest/Generators/CSharp/Azure.CSharp.Tests/AcceptanceTests.cs b/AutoRest/Generators/CSharp/Azure.CSharp.Tests/AcceptanceTests.cs index d04c712fd1c96..ec21cdd514493 100644 --- a/AutoRest/Generators/CSharp/Azure.CSharp.Tests/AcceptanceTests.cs +++ b/AutoRest/Generators/CSharp/Azure.CSharp.Tests/AcceptanceTests.cs @@ -527,8 +527,7 @@ public void AzureODataTests() Top = 10, OrderBy = "id" }; - var filterString = Uri.EscapeDataString("id gt 5 and name eq 'foo'"); - Assert.Equal(string.Format("$filter={0}&$orderby=id&$top=10", filterString), filter.ToString()); + Assert.Equal("$filter=id gt 5 and name eq 'foo'&$orderby=id&$top=10", filter.ToString()); client.Odata.GetWithFilter(filter); } } diff --git a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs index 639023af44855..c651bbdc33621 100644 --- a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs +++ b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs @@ -227,7 +227,6 @@ public void EncodingTheParameters() [Fact] public void ODataQuerySupportsAllParameters() { - var queryString = "foo eq 'bar'"; var query = new ODataQuery(p => p.Foo == "bar") { Expand = "param1", @@ -235,7 +234,7 @@ public void ODataQuerySupportsAllParameters() Skip = 10, Top = 100 }; - Assert.Equal(string.Format("$filter={0}&$orderby=d&$expand=param1&$top=100&$skip=10", Uri.EscapeDataString(queryString)), query.ToString()); + Assert.Equal("$filter=foo eq 'bar'&$orderby=d&$expand=param1&$top=100&$skip=10", query.ToString()); } [Fact] @@ -260,38 +259,34 @@ public void ODataQuerySupportsEmptyState() [Fact] public void ODataQuerySupportsPartialState() { - var queryString = "foo eq 'bar'"; var query = new ODataQuery(p => p.Foo == "bar") { Top = 100 }; - Assert.Equal(string.Format("$filter={0}&$top=100", Uri.EscapeDataString(queryString)), query.ToString()); + Assert.Equal("$filter=foo eq 'bar'&$top=100", query.ToString()); } [Fact] public void ODataQuerySupportsImplicitConversionFromFilterString() { - var queryString = "foo eq 'bar'"; - ODataQuery query = queryString; + ODataQuery query = "foo eq 'bar'"; query.Top = 100; - Assert.Equal(string.Format("$filter={0}&$top=100", Uri.EscapeDataString(queryString)), query.ToString()); + Assert.Equal("$filter=foo eq 'bar'&$top=100", query.ToString()); } [Fact] public void ODataQuerySupportsImplicitConversionFromFullFilterString() { - var queryString = "foo eq 'bar'"; - ODataQuery query = string.Format("$filter={0}", queryString); + ODataQuery query = "$filter=foo eq 'bar'"; query.Top = 100; - Assert.Equal(string.Format("$filter={0}&$top=100", Uri.EscapeDataString(queryString)), query.ToString()); + Assert.Equal("$filter=foo eq 'bar'&$top=100", query.ToString()); } [Fact] public void ODataQuerySupportsImplicitConversionFromQueryString() { - var queryString = "foo eq 'bar'"; - ODataQuery query = string.Format("$filter={0}&$top=100", queryString); - Assert.Equal(string.Format("$filter={0}&$top=100", Uri.EscapeDataString(queryString)), query.ToString()); + ODataQuery query = "$filter=foo eq 'bar'&$top=100"; + Assert.Equal("$filter=foo eq 'bar'&$top=100", query.ToString()); } [Fact] diff --git a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure/OData/ODataQuery.cs b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure/OData/ODataQuery.cs index 99fed407ca114..252c7739db668 100644 --- a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure/OData/ODataQuery.cs +++ b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure/OData/ODataQuery.cs @@ -146,17 +146,17 @@ public override string ToString() if (!string.IsNullOrEmpty(Filter)) { queryStringList.Add(string.Format(CultureInfo.InvariantCulture, - "$filter={0}", Uri.EscapeDataString(Filter))); + "$filter={0}", Filter)); } if (!string.IsNullOrEmpty(OrderBy)) { queryStringList.Add(string.Format(CultureInfo.InvariantCulture, - "$orderby={0}", Uri.EscapeDataString(OrderBy))); + "$orderby={0}", OrderBy)); } if (!string.IsNullOrEmpty(Expand)) { queryStringList.Add(string.Format(CultureInfo.InvariantCulture, - "$expand={0}", Uri.EscapeDataString(Expand))); + "$expand={0}", Expand)); } if (Top != null) { From 72ba2d63344acd09252ba06b58cbf4df82b7c516 Mon Sep 17 00:00:00 2001 From: Hovsep Mkrtchyan Date: Fri, 22 Apr 2016 17:23:18 -0700 Subject: [PATCH 15/16] Added tests to Odata filter generation --- .../ODataTests.cs | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs index c651bbdc33621..7c6829b301d14 100644 --- a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs +++ b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs @@ -7,6 +7,7 @@ using Microsoft.Rest.Azure.OData; using Newtonsoft.Json; using Xunit; +using System.Collections.Generic; namespace Microsoft.Rest.ClientRuntime.Azure.Test { @@ -248,14 +249,43 @@ public void ODataQuerySupportsEmptyState() { Value = null }; + var paramEncoded = new InputParam1 + { + Value = "bar/car" + }; query = new ODataQuery(p => p.Foo == param.Value); Assert.Equal("", query.ToString()); query = new ODataQuery(p => p.Foo == param.Value && p.AssignedTo(param.Value)); Assert.Equal("", query.ToString()); query = new ODataQuery(p => p.AssignedTo(param.Value)); Assert.Equal("", query.ToString()); + query = new ODataQuery(p => p.AssignedTo(paramEncoded.Value)); + Assert.Equal("$filter=assignedTo('bar%2Fcar')", query.ToString()); + } + + [Fact] + public void ODataQuerySupportsCustomDateTimeOffsetFilter() + { + var param = new Param1 + { + SubmitTime = DateTimeOffset.Parse("2016-03-28T08:15:00.0971693+00:00"), + State = "Ended" + + }; + + var filter = new List(); + filter.Add(string.Format("submitTime lt datetimeoffset'{0}'", Uri.EscapeDataString(param.SubmitTime.Value.ToString("O")))); + var filterString = string.Join(" and ", filter.ToArray()); + + + var query = new ODataQuery + { + Filter = filterString + }; + Assert.Equal("$filter=submitTime lt datetimeoffset'2016-03-28T08%3A15%3A00.0971693%2B00%3A00'' and state ne 'Ended'", query.ToString()); } + [Fact] public void ODataQuerySupportsPartialState() { @@ -266,6 +296,17 @@ public void ODataQuerySupportsPartialState() Assert.Equal("$filter=foo eq 'bar'&$top=100", query.ToString()); } + [Fact] + public void ODataQuerySupportsPartialStateWithSlashes() + { + var queryString = "$filter=foo eq 'bar%2Fclub'&$top=100"; + var query = new ODataQuery(p => p.Foo == "bar/club") + { + Top = 100 + }; + Assert.Equal(queryString, query.ToString()); + } + [Fact] public void ODataQuerySupportsImplicitConversionFromFilterString() { @@ -318,7 +359,7 @@ public void ODataQuerySupportsMethod() var filterString = FilterString.Generate(parameters => parameters.AtScope() && parameters.AssignedTo(param.Value)); - Assert.Equal(filterString, "atScope() and assignedTo('Microsoft.Web%2Fsites')"); + Assert.Equal("atScope() and assignedTo('Microsoft.Web%2Fsites')", filterString); } } @@ -356,6 +397,10 @@ public class Param1 [JsonProperty("d")] public DateTime Date { get; set; } public DateTime Date2 { get; set; } + [JsonProperty("submitTime")] + public DateTimeOffset? SubmitTime { get; set; } + [JsonProperty("state")] + public string State { get; set; } [JsonProperty("vals")] public string[] Values { get; set; } [JsonProperty("param2")] From af6fa32b56c6b08589aa558f71dfee6eb9dbb841 Mon Sep 17 00:00:00 2001 From: Hovsep Mkrtchyan Date: Fri, 22 Apr 2016 18:15:07 -0700 Subject: [PATCH 16/16] Fixed the test --- .../Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs index 7c6829b301d14..e267142696404 100644 --- a/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs +++ b/ClientRuntimes/CSharp/Microsoft.Rest.ClientRuntime.Azure.Tests/ODataTests.cs @@ -275,6 +275,7 @@ public void ODataQuerySupportsCustomDateTimeOffsetFilter() var filter = new List(); filter.Add(string.Format("submitTime lt datetimeoffset'{0}'", Uri.EscapeDataString(param.SubmitTime.Value.ToString("O")))); + filter.Add(string.Format("state ne '{0}'", param.State)); var filterString = string.Join(" and ", filter.ToArray()); @@ -282,7 +283,7 @@ public void ODataQuerySupportsCustomDateTimeOffsetFilter() { Filter = filterString }; - Assert.Equal("$filter=submitTime lt datetimeoffset'2016-03-28T08%3A15%3A00.0971693%2B00%3A00'' and state ne 'Ended'", query.ToString()); + Assert.Equal("$filter=submitTime lt datetimeoffset'2016-03-28T08%3A15%3A00.0971693%2B00%3A00' and state ne 'Ended'", query.ToString()); }