From c5b1736043e2bd4556e5344d799615565c03d535 Mon Sep 17 00:00:00 2001 From: Vlad Barosan Date: Tue, 20 Mar 2018 15:01:47 -0700 Subject: [PATCH 01/10] Refactoring and missing properties --- src/CodeNamerGo.cs | 272 +++++++++++++++-------------------- src/Extensions.cs | 63 +++++--- src/Model/CodeModelGo.cs | 75 +++------- src/Model/CompositeTypeGo.cs | 57 ++++++-- 4 files changed, 223 insertions(+), 244 deletions(-) diff --git a/src/CodeNamerGo.cs b/src/CodeNamerGo.cs index 9df9a37fd..0df83e6ac 100644 --- a/src/CodeNamerGo.cs +++ b/src/CodeNamerGo.cs @@ -16,29 +16,19 @@ namespace AutoRest.Go { public class CodeNamerGo : CodeNamer { - public new static CodeNamerGo Instance - { - get - { - return (CodeNamerGo)CodeNamer.Instance; - } - } + public new static CodeNamerGo Instance => (CodeNamerGo)CodeNamer.Instance; - public virtual IEnumerable PipelineImports - { - get - { - return new string[] { PrimaryTypeGo.GetImportLine(package: PipelineImportPath) }; - } - } + public virtual IEnumerable PipelineImports => new [] { PrimaryTypeGo.GetImportLine(package: PipelineImportPath) }; public virtual IEnumerable StandardImports { get { - var imports = new List(); - imports.Add(PrimaryTypeGo.GetImportLine(package: "net/http")); - imports.Add(PrimaryTypeGo.GetImportLine(package: "context")); + var imports = new List + { + PrimaryTypeGo.GetImportLine(package: "net/http"), + PrimaryTypeGo.GetImportLine(package: "context") + }; return imports; } } @@ -48,48 +38,6 @@ public virtual IEnumerable StandardImports PrimaryTypeGo.GetImportLine(package: "net/http") }; - // CommonInitialisms are those "words" within a name that Golint expects to be uppercase. - // See https://github.com/golang/lint/blob/master/lint.go for detail. - private HashSet CommonInitialisms => new HashSet(StringComparer.OrdinalIgnoreCase) { - "Acl", - "Api", - "Ascii", - "Cpu", - "Css", - "Dns", - "Eof", - "Guid", - "Html", - "Http", - "Https", - "Id", - "Ip", - "Json", - "Lhs", - "Qps", - "Ram", - "Rhs", - "Rpc", - "Sla", - "Smtp", - "Sql", - "Ssh", - "Tcp", - "Tls", - "Ttl", - "Udp", - "Ui", - "Uid", - "Uuid", - "Uri", - "Url", - "Utf8", - "Vm", - "Xml", - "Xsrf", - "Xss", - }; - public string[] UserDefinedNames => new string[] { "UserAgent", "Version", @@ -273,7 +221,6 @@ public static string AttachTypeName(string name, string packageName, bool nameIn /// Refactor -> Namer ... Even better if this already exists in the core :D /// /// - /// /// The formatted string public override string PascalCase(string name) { @@ -283,59 +230,39 @@ public override string PascalCase(string name) } return - name.Split(new Char[]{'.', '_', '@', '-', ' ', '$'}) + name.Split('.', '_', '@', '-', ' ', '$') .Where(s => !string.IsNullOrEmpty(s)) .Select(s => char.ToUpperInvariant(s[0]) + s.Substring(1, s.Length - 1)) .DefaultIfEmpty("") .Aggregate(string.Concat); } - public override string GetEnumMemberName(string name) - { - return EnsureNameCase(base.GetEnumMemberName(name)); - } + public override string GetEnumMemberName(string name) => EnsureNameCase(base.GetEnumMemberName(name)); - public override string GetFieldName(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return name; - } - return EnsureNameCase(RemoveInvalidCharacters(PascalCase(GetEscapedReservedName(name, "Field")))); - } + public override string GetFieldName(string name) => + string.IsNullOrWhiteSpace(name) ? + name : + EnsureNameCase(RemoveInvalidCharacters(PascalCase(GetEscapedReservedName(name, "Field")))); - public override string GetInterfaceName(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return name; - } - return EnsureNameCase(RemoveInvalidCharacters(PascalCase(name))); - } + public override string GetInterfaceName(string name) => + string.IsNullOrWhiteSpace(name) ? + name : + EnsureNameCase(RemoveInvalidCharacters(PascalCase(name))); /// /// Formats a string for naming a method using Pascal case by default. /// /// /// The formatted string. - public override string GetMethodName(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return name; - } - return EnsureNameCase(GetEscapedReservedName(RemoveInvalidCharacters(PascalCase(name)), "Method")); - } + public override string GetMethodName(string name) => + string.IsNullOrWhiteSpace(name) ? + name : + EnsureNameCase(GetEscapedReservedName(RemoveInvalidCharacters(PascalCase(name)), "Method")); - public override string GetMethodGroupName(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return name; - } - - return EnsureNameCase(RemoveInvalidCharacters(PascalCase(name))); - } + public override string GetMethodGroupName(string name) => + string.IsNullOrWhiteSpace(name) ? + name : + EnsureNameCase(RemoveInvalidCharacters(PascalCase(name))); /// /// Formats a string for naming method parameters using Camel case by default. @@ -348,7 +275,7 @@ public override string GetParameterName(string name) { return name; } - if (Extensions.StartsWithAcronym(name)) + if (name.StartsWithAcronym()) { return EnsureNameCase(GetEscapedReservedName((RemoveInvalidCharacters(name).ToLower()), "Parameter")); } @@ -360,42 +287,30 @@ public override string GetParameterName(string name) /// /// /// The formatted string. - public override string GetPropertyName(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return name; - } - return EnsureNameCase(GetEscapedReservedName(RemoveInvalidCharacters(PascalCase(name)), "Property")); - } + public override string GetPropertyName(string name) => + string.IsNullOrWhiteSpace(name) ? + name : + EnsureNameCase(GetEscapedReservedName(RemoveInvalidCharacters(PascalCase(name)), "Property")); /// /// Formats a string for naming a Type or Object using Pascal case by default. /// /// /// The formatted string. - public override string GetTypeName(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return name; - } - return EnsureNameCase(GetEscapedReservedName(RemoveInvalidCharacters(PascalCase(name)), "Type")); - } + public override string GetTypeName(string name) => + string.IsNullOrWhiteSpace(name) ? + name : + EnsureNameCase(GetEscapedReservedName(RemoveInvalidCharacters(PascalCase(name)), "Type")); /// /// Formats a string for naming a local variable using Camel case by default. /// /// /// The formatted string. - public override string GetVariableName(string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - return name; - } - return EnsureNameCase(GetEscapedReservedName(CamelCase(RemoveInvalidCharacters(name)), "Var")); - } + public override string GetVariableName(string name) => + string.IsNullOrWhiteSpace(name) ? + name : + EnsureNameCase(GetEscapedReservedName(CamelCase(RemoveInvalidCharacters(name)), "Var")); /// /// Converts names the conflict with Go reserved terms by appending the passed appendValue. @@ -407,12 +322,12 @@ protected override string GetEscapedReservedName(string name, string appendValue { if (name == null) { - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); } if (appendValue == null) { - throw new ArgumentNullException("appendValue"); + throw new ArgumentNullException(nameof(appendValue)); } // Use case-sensitive comparisons to reduce generated names @@ -465,38 +380,45 @@ public override string EscapeDefaultValue(string defaultValue, IModelType type) { if (type == null) { - throw new ArgumentNullException("type"); + throw new ArgumentNullException(nameof(type)); } - PrimaryType primaryType = type as PrimaryType; - if (defaultValue != null) + var primaryType = type as PrimaryType; + + if (defaultValue == null) { - if (type is CompositeType) - { - return type.Name + "{}"; - } - else if (primaryType != null) - { - if (primaryType.KnownPrimaryType == KnownPrimaryType.String - || primaryType.KnownPrimaryType == KnownPrimaryType.Uuid - || primaryType.KnownPrimaryType == KnownPrimaryType.TimeSpan - || primaryType.IsDateTimeType()) - { - return CodeNamer.Instance.QuoteValue(defaultValue); - } - else if (primaryType.KnownPrimaryType == KnownPrimaryType.Boolean) - { - return defaultValue.ToLowerInvariant(); - } - else if (primaryType.KnownPrimaryType == KnownPrimaryType.ByteArray) - { - return "[]byte(\"" + defaultValue + "\")"; - } - else - { - //TODO: handle imports for package types. - } - } + return null; + } + + if (type is CompositeType) + { + return type.Name + "{}"; + } + + if (primaryType == null) + { + return defaultValue; } + + if (primaryType.KnownPrimaryType == KnownPrimaryType.String + || primaryType.KnownPrimaryType == KnownPrimaryType.Uuid + || primaryType.KnownPrimaryType == KnownPrimaryType.TimeSpan + || primaryType.IsDateTimeType()) + { + return CodeNamer.Instance.QuoteValue(defaultValue); + } + else if (primaryType.KnownPrimaryType == KnownPrimaryType.Boolean) + { + return defaultValue.ToLowerInvariant(); + } + else if (primaryType.KnownPrimaryType == KnownPrimaryType.ByteArray) + { + return "[]byte(\"" + defaultValue + "\")"; + } + else + { + //TODO: handle imports for package types. + } + return defaultValue; } @@ -538,5 +460,47 @@ public string PipelineImportPath /// Gets the type name for the etag type. /// public string ETagTypeName => "ETag"; + + // CommonInitialisms are those "words" within a name that Golint expects to be uppercase. + // See https://github.com/golang/lint/blob/master/lint.go for detail. + private HashSet CommonInitialisms => new HashSet(StringComparer.OrdinalIgnoreCase) { + "Acl", + "Api", + "Ascii", + "Cpu", + "Css", + "Dns", + "Eof", + "Guid", + "Html", + "Http", + "Https", + "Id", + "Ip", + "Json", + "Lhs", + "Qps", + "Ram", + "Rhs", + "Rpc", + "Sla", + "Smtp", + "Sql", + "Ssh", + "Tcp", + "Tls", + "Ttl", + "Udp", + "Ui", + "Uid", + "Uuid", + "Uri", + "Url", + "Utf8", + "Vm", + "Xml", + "Xsrf", + "Xss", + }; } } diff --git a/src/Extensions.cs b/src/Extensions.cs index cef03c17d..7ae5d1315 100644 --- a/src/Extensions.cs +++ b/src/Extensions.cs @@ -43,7 +43,7 @@ public static CodeModelGo Cast(this CodeModel cm) } /// - /// This method changes string to sentence where is make the first word + /// This method changes string to sentence where is make the first word /// of sentence to lowercase (unless it is an acronym). The sentence is coming directly from swagger. /// /// @@ -54,15 +54,13 @@ public static string ToSentence(this string value) { return string.Empty; } - else + + value = value.Trim(); + if (value.StartsWithAcronym()) { - value = value.Trim(); - if (value.StartsWithAcronym()) - { - return value; - } - return value.First().ToString().ToLowerInvariant() + (value.Length > 1 ? value.Substring(1) : ""); + return value; } + return value.First().ToString().ToLowerInvariant() + (value.Length > 1 ? value.Substring(1) : ""); } /// @@ -74,7 +72,7 @@ public static string ToSentence(this string value) public static bool StartsWithAcronym(this string value) { string firstWord = value.Trim().Split(' ', '-', '_').First(); - return firstWord.Length > 1 && firstWord.All(c => char.IsUpper(c)); + return firstWord.Length > 1 && firstWord.All(char.IsUpper); } /// @@ -126,7 +124,7 @@ public static string ToShortName(this string longName) } /// - /// This method checks if MethodGroupName is plural of package name. + /// This method checks if MethodGroupName is plural of package name. /// It returns false for packages not listed in dictionary 'plural'. /// Example, group EventHubs in package EventHub. /// Refactor -> Namer, but also could be used by the CodeModelTransformer @@ -296,24 +294,43 @@ public static bool CanBeNull(this IModelType type) /// public static void AddImports(this IModelType type, HashSet imports) { - if (type is DictionaryTypeGo) + switch (type) { - (type as DictionaryTypeGo).AddImports(imports); - } - else if (type is PrimaryTypeGo) - { - (type as PrimaryTypeGo).AddImports(imports); - } - else if (type is SequenceTypeGo) - { - (type as SequenceTypeGo).AddImports(imports); + case DictionaryTypeGo dictionaryType: + dictionaryType.AddImports(imports); + break; + case PrimaryTypeGo primaryType: + primaryType.AddImports(imports); + break; + default: + (type as SequenceTypeGo)?.AddImports(imports); + break; } } public static bool ShouldBeSyntheticType(this IModelType type) - { - return (type is PrimaryType || type is SequenceType || type is DictionaryType || type is EnumType || - (type is CompositeTypeGo && (type as CompositeTypeGo).IsPolymorphicResponse())); + { + return (type is PrimaryType || type is SequenceType || type is DictionaryType || type is EnumType || + (type is CompositeTypeGo && ((CompositeTypeGo) type).IsPolymorphicResponse())); + } + + /// + /// Gets if the type has an interface. + /// + public static bool HasInterface(this IModelType type) + { + return (type is CompositeTypeGo compositeType) && + (compositeType.IsRootType || compositeType.BaseIsPolymorphic && !compositeType.IsLeafType); + } + + /// + /// Gets the interface name for the type. + /// + /// + /// + public static string GetInterfaceName(this IModelType type) + { + return $"Basic{type.Name}"; } /// diff --git a/src/Model/CodeModelGo.cs b/src/Model/CodeModelGo.cs index 1723f6403..e1e8ae915 100644 --- a/src/Model/CodeModelGo.cs +++ b/src/Model/CodeModelGo.cs @@ -20,13 +20,7 @@ public class CodeModelGo : CodeModel public string Version { get; } - public string UserAgent - { - get - { - return $"Azure-SDK-For-Go/{Version} arm-{Namespace}/{ApiVersion}"; - } - } + public string UserAgent => $"Azure-SDK-For-Go/{Version} arm-{Namespace}/{ApiVersion}"; public CodeModelGo() { @@ -37,33 +31,19 @@ public CodeModelGo() public override string Namespace { - get - { - if (string.IsNullOrEmpty(base.Namespace)) - { - return base.Namespace; - } - - return base.Namespace.ToLowerInvariant(); - } - set - { - base.Namespace = value; - } + get => string.IsNullOrEmpty(base.Namespace) ? base.Namespace : base.Namespace.ToLowerInvariant(); + set => base.Namespace = value; } public string ServiceName => CodeNamer.Instance.PascalCase(Namespace ?? string.Empty); - public string GetDocumentation() - { - return $"Package {Namespace} implements the Azure ARM {ServiceName} service API version {ApiVersion}.\n\n{(Documentation ?? string.Empty).UnwrapAnchorTags()}"; - } + public string GetDocumentation() => $"Package {Namespace} implements the Azure ARM {ServiceName} service API version {ApiVersion}.\n\n{(Documentation ?? string.Empty).UnwrapAnchorTags()}"; public string BaseClient => CodeNamerGo.Instance.ExportClientTypes ? "ManagementClient" : "managementClient"; public bool IsCustomBaseUri => Extensions.ContainsKey(SwaggerExtensions.ParameterizedHostExtension); - public string APIType => (string)Settings.Instance.Host?.GetValue("openapi-type").Result; + public string APIType => Settings.Instance.Host?.GetValue("openapi-type").Result; public IEnumerable ClientImports { @@ -71,7 +51,7 @@ public IEnumerable ClientImports { var imports = new HashSet(); imports.UnionWith(CodeNamerGo.Instance.PipelineImports); - var clientMg = MethodGroups.Where(mg => string.IsNullOrEmpty(mg.Name)).FirstOrDefault(); + var clientMg = MethodGroups.FirstOrDefault(mg => string.IsNullOrEmpty(mg.Name)); if (clientMg != null) { imports.UnionWith(clientMg.Imports); @@ -96,10 +76,12 @@ public IEnumerable ModelImports var addStrConvImport = false; var addBase64Import = false; // Create an ordered union of the imports each model requires - var imports = new HashSet(); - imports.Add(PrimaryTypeGo.GetImportLine(package: "net/http")); - imports.Add(PrimaryTypeGo.GetImportLine(package: "reflect")); - imports.Add(PrimaryTypeGo.GetImportLine(package: "strings")); + var imports = new HashSet + { + PrimaryTypeGo.GetImportLine(package: "net/http"), + PrimaryTypeGo.GetImportLine(package: "reflect"), + PrimaryTypeGo.GetImportLine(package: "strings") + }; ModelTypes.Cast() .ForEach(mt => @@ -269,14 +251,9 @@ public string HelperAllGlobalParameters } } - public IEnumerable ClientMethods - { - get - { + public IEnumerable ClientMethods => // client methods are the ones with no method group - return Methods.Cast().Where(m => string.IsNullOrEmpty(m.MethodGroup.Name)); - } - } + Methods.Cast().Where(m => string.IsNullOrEmpty(m.MethodGroup.Name)); /// FormatVersion normalizes a version string into a SemVer if it resembles one. Otherwise, /// it returns the original string unmodified. If version is empty or only comprised of @@ -313,36 +290,18 @@ public static string FormatVersion(string version) /// /// Returns true if any model types contain a metadata property. /// - public bool UsesMetadataType - { - get - { - return ModelTypes.Where(m => m.Properties.Cast().Where(p => p.IsMetadata).Any()).Any(); - } - } + public bool UsesMetadataType => ModelTypes.Any(m => m.Properties.Cast().Any(p => p.IsMetadata)); /// /// Returns true if any model types contain an Etag property. /// - public bool UsesETags - { - get - { - return ModelTypes.Where(m => m.Properties.Where(p => p.ModelType.IsETagType()).Any()).Any(); - } - } + public bool UsesETags => ModelTypes.Any(m => m.Properties.Any(p => p.ModelType.IsETagType())); /// /// Returns a collection of composite types that require custom marshalling and/or /// unmarshalling. Can be empty if there are no types requriring marshallers. /// - public IEnumerable RequiresMarshallers - { - get - { - return ModelTypes.Cast().Where(m => m.Properties.Where(p => p.ModelType.IsDateTimeType()).Any()); - } - } + public IEnumerable RequiresMarshallers => ModelTypes.Cast().Where(m => m.Properties.Any(p => p.ModelType.IsDateTimeType())); /// /// Returns the encoding type used for serialization (e.g. xml or json). diff --git a/src/Model/CompositeTypeGo.cs b/src/Model/CompositeTypeGo.cs index daed5f90c..d753aa856 100644 --- a/src/Model/CompositeTypeGo.cs +++ b/src/Model/CompositeTypeGo.cs @@ -18,7 +18,9 @@ public class CompositeTypeGo : CompositeType { private bool _wrapper; - // True if the type is returned by a method + /// + /// True if the type is returned by a method + /// public bool IsResponseType; // Name of the field containing the URL used to retrieve the next result set @@ -27,6 +29,8 @@ public class CompositeTypeGo : CompositeType public bool PreparerNeeded = false; + public string DiscriminatorEnumValue => DiscriminatorEnum.Values.FirstOrDefault(c => c.SerializedName.Equals(SerializedName)).Name; + public IEnumerable DerivedTypes => CodeModel.ModelTypes.Where(t => t.DerivesFrom(this)); public IEnumerable SiblingTypes @@ -46,7 +50,7 @@ public bool HasPolymorphicFields { get { - return AllProperties.Any(p => + return AllProperties.Any(p => // polymorphic composite (p.ModelType is CompositeType && (p.ModelType as CompositeTypeGo).IsPolymorphic) || // polymorphic array @@ -57,7 +61,7 @@ public bool HasPolymorphicFields public EnumType DiscriminatorEnum; - public string DiscriminatorEnumValue => DiscriminatorEnum.Values.FirstOrDefault(c => c.SerializedName.Equals(SerializedName)).Name; + private CompositeTypeGo _rootType; public CompositeTypeGo() { @@ -95,10 +99,12 @@ public CompositeTypeGo(MethodGo responseToWrap) if (!wrappedType.IsPrimaryType(KnownPrimaryType.Stream)) { // add the wrapped type as a property named Value - var p = new PropertyGo(); - p.Name = "Value"; - p.SerializedName = "value"; - p.ModelType = wrappedType; + var p = new PropertyGo + { + Name = "Value", + SerializedName = "value", + ModelType = wrappedType + }; Add(p); } @@ -123,7 +129,7 @@ private void AddPolymorphicPropertyIfNecessary() SerializedName = PolymorphicDiscriminator, ModelType = DiscriminatorEnum, })); - } + } } public string PolymorphicProperty @@ -154,6 +160,39 @@ public IEnumerable AllProperties } } + /// + /// Gets the root type of the inheritance chain. + /// + public CompositeTypeGo RootType + { + get + { + if (_rootType == null) + { + + CompositeType rootModelType = this; + while (rootModelType.BaseModelType?.BaseIsPolymorphic == true) + { + rootModelType = rootModelType.BaseModelType; + } + + _rootType = rootModelType as CompositeTypeGo; + } + + return _rootType; + } + } + + /// + /// Gets if the type is a root type in an inheritance chain. + /// + public bool IsRootType => IsPolymorphic && RootType == this; + + /// + /// Gets if the type is a leaf type in an inheritance chain. + /// + public bool IsLeafType => BaseIsPolymorphic && DerivedTypes.IsNullOrEmpty(); + public override Property Add(Property item) { var property = base.Add(item) as PropertyGo; @@ -206,7 +245,7 @@ public string Fields(bool forMarshaller) AddPolymorphicPropertyIfNecessary(); var indented = new IndentedStringBuilder(" "); - // check if the XML name matches tyhe type name. + // check if the XML name matches the type name. // if it doesn't then add an XMLName field. if (NeedsXmlNameField) { From 238fe60d64938d98da738931824581cfb0af01e2 Mon Sep 17 00:00:00 2001 From: Vlad Barosan Date: Tue, 20 Mar 2018 16:10:27 -0700 Subject: [PATCH 02/10] make model changes --- src/Model/CompositeTypeGo.cs | 105 +++++++++++++++------------------- src/Model/MethodGo.cs | 107 ++++++++++++++--------------------- src/Model/ParameterGo.cs | 2 +- src/Model/PropertyGo.cs | 73 +++++++++++++++--------- src/Model/SequenceTypeGo.cs | 7 ++- src/TransformerGo.cs | 18 ++++++ 6 files changed, 158 insertions(+), 154 deletions(-) diff --git a/src/Model/CompositeTypeGo.cs b/src/Model/CompositeTypeGo.cs index d753aa856..604325f6e 100644 --- a/src/Model/CompositeTypeGo.cs +++ b/src/Model/CompositeTypeGo.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using AutoRest.Core.Utilities.Collections; using static AutoRest.Core.Utilities.DependencyInjection; namespace AutoRest.Go.Model @@ -16,15 +17,18 @@ namespace AutoRest.Go.Model /// public class CompositeTypeGo : CompositeType { - private bool _wrapper; + public EnumType DiscriminatorEnum; + + private CompositeTypeGo _rootType; /// /// True if the type is returned by a method /// public bool IsResponseType; - // Name of the field containing the URL used to retrieve the next result set - // (null or empty if the model is not paged). + /// + /// Name of the field containing the URL used to retrieve the next result set (null or empty if the model is not paged). + /// public string NextLink; public bool PreparerNeeded = false; @@ -37,12 +41,14 @@ public IEnumerable SiblingTypes { get { - var st = (BaseModelType as CompositeTypeGo).DerivedTypes; - if (BaseModelType.BaseModelType != null && BaseModelType.BaseModelType.IsPolymorphic) + var siblingTypes = RootType.DerivedTypes; + + if (RootType.IsPolymorphic) { - st = st.Union((BaseModelType as CompositeTypeGo).SiblingTypes); + siblingTypes = siblingTypes.ConcatSingleItem(RootType); } - return st; + + return siblingTypes; } } @@ -51,26 +57,20 @@ public bool HasPolymorphicFields get { return AllProperties.Any(p => - // polymorphic composite - (p.ModelType is CompositeType && (p.ModelType as CompositeTypeGo).IsPolymorphic) || + // polymorphic composite + p.ModelType.HasInterface() || // polymorphic array - (p.ModelType is SequenceType && (p.ModelType as SequenceTypeGo).ElementType is CompositeType && - ((p.ModelType as SequenceTypeGo).ElementType as CompositeType).IsPolymorphic)); + (p.ModelType is SequenceType sequenceType && + sequenceType.ElementType.HasInterface())); } } - public EnumType DiscriminatorEnum; - - private CompositeTypeGo _rootType; - public CompositeTypeGo() { - } public CompositeTypeGo(string name) : base(name) { - } public CompositeTypeGo(MethodGo responseToWrap) @@ -107,8 +107,8 @@ public CompositeTypeGo(MethodGo responseToWrap) }; Add(p); } - - _wrapper = true; + AddPolymorphicPropertyIfNecessary(); + IsWrapperType = true; } /// @@ -119,46 +119,31 @@ public CompositeTypeGo(MethodGo responseToWrap) /// /// If PolymorphicDiscriminator is set, makes sure we have a PolymorphicDiscriminator property. /// - private void AddPolymorphicPropertyIfNecessary() + internal void AddPolymorphicPropertyIfNecessary() { if (!string.IsNullOrEmpty(PolymorphicDiscriminator) && Properties.All(p => p.SerializedName != PolymorphicDiscriminator)) { base.Add(New(new { - Name = CodeNamerGo.Instance.GetPropertyName(PolymorphicDiscriminator), + Name = PolymorphicDiscriminator, SerializedName = PolymorphicDiscriminator, - ModelType = DiscriminatorEnum, + ModelType = DiscriminatorEnum })); } } - public string PolymorphicProperty - { - get - { - if (!string.IsNullOrEmpty(PolymorphicDiscriminator)) - { - return CodeNamerGo.Instance.GetPropertyName(PolymorphicDiscriminator); - } - if (BaseModelType != null) - { - return (BaseModelType as CompositeTypeGo).PolymorphicProperty; - } - return null; - } - } + /// + /// Gets if there are any flattened fields. + /// + public bool HasFlattenedFields => Properties.Any(p => p.ModelType is CompositeTypeGo && p.ShouldBeFlattened()); - public IEnumerable AllProperties - { - get - { - if (BaseModelType != null) - { - return Properties.Cast().Concat((BaseModelType as CompositeTypeGo).AllProperties); - } - return Properties.Cast(); - } - } + public string PolymorphicProperty => !string.IsNullOrEmpty(PolymorphicDiscriminator) + ? CodeNamerGo.Instance.GetPropertyName(PolymorphicDiscriminator) + : ((CompositeTypeGo) BaseModelType).PolymorphicProperty; + + public IEnumerable AllProperties => BaseModelType != null ? + Properties.Cast().Concat(((CompositeTypeGo) BaseModelType).AllProperties) : + Properties.Cast(); /// /// Gets the root type of the inheritance chain. @@ -195,7 +180,7 @@ public CompositeTypeGo RootType public override Property Add(Property item) { - var property = base.Add(item) as PropertyGo; + var property = base.Add(item); AddPolymorphicPropertyIfNecessary(); return property; } @@ -267,8 +252,8 @@ public string Fields(bool forMarshaller) { indented.AppendFormat("// {0} - {1}\n", property.Name, property.Documentation); } - var enumType = property.ModelType as EnumTypeGo; - if (enumType != null && enumType.IsNamed) + + if (property.ModelType is EnumTypeGo enumType && enumType.IsNamed) { indented.AppendFormat("{0} {1} {2}\n", property.Name, @@ -278,7 +263,7 @@ public string Fields(bool forMarshaller) } else if (property.ModelType is DictionaryType) { - var typeName = (property.ModelType as DictionaryTypeGo).Name; + var typeName = ((DictionaryTypeGo) property.ModelType).Name; if (property.IsMetadata) { // use custom type instead of a map[string]string @@ -302,9 +287,9 @@ public string Fields(bool forMarshaller) // use custom type instead of *string indented.Append($"{NextLink} Marker `{CodeModel.ToCodeModelGo().Encoding}:\"{NextLink}\"`"); } - else if (property.ModelType is CompositeType && (property.ModelType as CompositeTypeGo).IsPolymorphic) + else if (property.ModelType is CompositeType && ((CompositeTypeGo) property.ModelType).IsPolymorphic) { - indented.AppendFormat("{0} {1} {2}\n", property.Name, property.ModelType.Name, property.Tag()); + indented.AppendFormat("{0} {1} {2}\n", property.Name, property.ModelType.GetInterfaceName(), property.Tag()); } else { @@ -331,21 +316,21 @@ public string Fields(bool forMarshaller) return indented.ToString(); } - public bool IsWrapperType => _wrapper; + public bool IsWrapperType { get; } - public IModelType BaseType { get; private set; } + public IModelType BaseType { get; } public IModelType GetElementType(IModelType type) { - if (type is SequenceTypeGo) + if (type is SequenceTypeGo sequenceType) { Name += "List"; - return GetElementType((type as SequenceType).ElementType); + return GetElementType(sequenceType.ElementType); } - else if (type is DictionaryTypeGo) + else if (type is DictionaryTypeGo dictionaryType) { Name += "Set"; - return GetElementType(((type as DictionaryTypeGo).ValueType)); + return GetElementType(dictionaryType.ValueType); } else { diff --git a/src/Model/MethodGo.cs b/src/Model/MethodGo.cs index d11639e8c..b0f639a0b 100644 --- a/src/Model/MethodGo.cs +++ b/src/Model/MethodGo.cs @@ -14,6 +14,7 @@ using System.Linq; using System.Net; using System.Text; +using Newtonsoft.Json.Linq; namespace AutoRest.Go.Model { @@ -21,13 +22,13 @@ public class MethodGo : Method { private const string DefaultResponseType = "http.Response"; - public new MethodGroupGo MethodGroup { get { return (MethodGroupGo)base.MethodGroup; } } + public new MethodGroupGo MethodGroup => (MethodGroupGo)base.MethodGroup; public string PackageName { get; private set; } public string APIVersion { get; private set; } - private readonly string lroDescription = " This method may poll for completion. Polling can be canceled by passing the cancel channel argument. " + + private const string lroDescription = " This method may poll for completion. Polling can be canceled by passing the cancel channel argument. " + "The channel will be used to cancel polling and any outstanding HTTP requests."; public bool NextAlreadyDefined { get; private set; } @@ -91,18 +92,18 @@ from p in Parameters } public string MethodSignature => $"{Name}({MethodParametersSignature(false)})"; - + public string MethodParametersSignatureComplete { get - { + { var signature = new StringBuilder("("); signature.Append(MethodParametersSignature(false)); if (!IsLongRunningOperation()) { if (MethodParametersSignature(false).Length > 0) { - signature.Append( ", "); + signature.Append(", "); } signature.Append("cancel <-chan struct{}"); } @@ -151,7 +152,7 @@ public PropertyGo ListElement get { var body = ReturnType.Body as CompositeTypeGo; - return body.Properties.Where(p => p.ModelType is SequenceTypeGo).FirstOrDefault() as PropertyGo; + return body.Properties.FirstOrDefault(p => p.ModelType is SequenceTypeGo) as PropertyGo; } } @@ -162,7 +163,7 @@ public PropertyGo ListElement /// public string MethodParametersSignature(bool includeCtx) { - List declarations = new List(); + var declarations = new List(); if (includeCtx) { @@ -178,9 +179,12 @@ public string MethodParametersSignature(bool includeCtx) declarations.Add("body io.ReadSeeker"); continue; } + declarations.Add(string.Format(localParam.IsPassedByValue() ? "{0} {1}" - : "{0} *{1}", localParam.Name, localParam.ModelType.Name)); + : "{0} *{1}", localParam.Name, localParam.ModelType.HasInterface() + ? localParam.ModelType.GetInterfaceName() + : localParam.ModelType.Name.ToString())); } return string.Join(", ", declarations); @@ -198,24 +202,15 @@ public string MethodReturnType { return rv.Body.Name.ToString(); } - else if (rv.Headers != null) - { - return rv.Headers.Name.ToString(); - } - return DefaultResponseType; + + return rv.Headers != null ? rv.Headers.Name.ToString() : DefaultResponseType; } } /// /// Returns true if the method return type is the default response. /// - public bool IsDefaultResponseType - { - get - { - return MethodReturnType == DefaultResponseType; - } - } + public bool IsDefaultResponseType => MethodReturnType == DefaultResponseType; /// /// Returns the method return signature for this method (e.g. "foo, bar"). @@ -419,14 +414,13 @@ public IEnumerable SendDecorators { get { - var decorators = new List(); - decorators.Add("req"); - if (RegisterRP) + var decorators = new List { - decorators.Add("azure.DoRetryWithRegistration(client.Client)"); - } else { - decorators.Add("autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)"); - } + "req", + RegisterRP + ? "azure.DoRetryWithRegistration(client.Client)" + : "autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...)" + }; if (IsLongRunningOperation()) { decorators.Add("azure.DoPollForAsynchronous(client.PollingDelay)"); @@ -439,10 +433,12 @@ public IEnumerable RespondDecorators { get { - var decorators = new List(); - decorators.Add("resp"); - decorators.Add("client.ByInspecting()"); - decorators.Add(string.Format("azure.WithErrorUnlessStatusCode({0})", string.Join(",", ResponseCodes.ToArray()))); + var decorators = new List + { + "resp", + "client.ByInspecting()", + string.Format("azure.WithErrorUnlessStatusCode({0})", string.Join(",", ResponseCodes.ToArray())) + }; var rvNeedsUnmarshalling = ReturnValueRequiresUnmarshalling(); if (rvNeedsUnmarshalling && !ReturnValue().Body.IsStreamType()) @@ -466,15 +462,9 @@ public IEnumerable RespondDecorators } } - public string Response - { - get - { - return HasReturnValue() - ? "result.Response = autorest.Response{Response: resp}" - : "result.Response = resp"; - } - } + public string Response => HasReturnValue() + ? "result.Response = autorest.Response{Response: resp}" + : "result.Response = resp"; /// /// Check if method has a return response. @@ -510,8 +500,7 @@ public bool ReturnValueIsXmlWrapped() return false; } - var ctg = ReturnValue().Body as CompositeTypeGo; - if (ctg == null) + if (!(ReturnValue().Body is CompositeTypeGo ctg)) { return false; } @@ -540,7 +529,7 @@ public bool BodyParamNeedsMarshalling() } /// - /// Checks if method has pageable extension (x-ms-pageable) enabled. + /// Checks if method has pageable extension (x-ms-pageable) enabled. /// /// @@ -558,7 +547,7 @@ public bool NextMethodExists(IEnumerable methods) string next = NextOperationName; if (string.IsNullOrEmpty(next)) { - return false; + return false; } return methods.Any(m => m.Name.Value.EqualsIgnoreCase(next)); } @@ -579,16 +568,10 @@ public MethodGo NextMethod } } - public string NextOperationName - { - get - { - return NextMethod?.Name.Value; - } - } + public string NextOperationName => NextMethod?.Name.Value; /// - /// Check if method has long running extension (x-ms-long-running-operation) enabled. + /// Check if method has long running extension (x-ms-long-running-operation) enabled. /// /// public bool IsLongRunningOperation() @@ -626,19 +609,17 @@ public string NextLink // Note: // Methods can be paged, even if "nextLinkName" is null // Paged method just means a method returns an array - if (Extensions.ContainsKey(AzureExtensions.PageableExtension)) + if (!Extensions.ContainsKey(AzureExtensions.PageableExtension)) { - var pageableExtension = Extensions[AzureExtensions.PageableExtension] as Newtonsoft.Json.Linq.JContainer; - if (pageableExtension != null) - { - var nextLink = (string)pageableExtension["nextLinkName"]; - if (!string.IsNullOrEmpty(nextLink)) - { - return CodeNamerGo.Instance.GetPropertyName(nextLink); - } - } + return null; } - return null; + if (!(Extensions[AzureExtensions.PageableExtension] is JContainer pageableExtension)) + { + return null; + } + + var nextLink = (string)pageableExtension["nextLinkName"]; + return !string.IsNullOrEmpty(nextLink) ? CodeNamerGo.Instance.GetPropertyName(nextLink) : null; } } diff --git a/src/Model/ParameterGo.cs b/src/Model/ParameterGo.cs index e03f3f174..bf1377e74 100644 --- a/src/Model/ParameterGo.cs +++ b/src/Model/ParameterGo.cs @@ -373,7 +373,7 @@ public static string BuildParameterMap(this IEnumerable parameters, builder.Append(mapVariable); builder.Append(" := map[string]interface{} {"); - if (parameters.Count() > 0) + if (parameters.Any()) { builder.AppendLine(); var indented = new IndentedStringBuilder(" "); diff --git a/src/Model/PropertyGo.cs b/src/Model/PropertyGo.cs index 2ea8a0423..b10daf2b1 100644 --- a/src/Model/PropertyGo.cs +++ b/src/Model/PropertyGo.cs @@ -13,10 +13,9 @@ public class PropertyGo : Property { public PropertyGo() { - } - public string Tag() + public string Tag(bool omitEmpty = true) { // don't emit a tag if this property is part of a parameter group (it's not necessary) if (Extensions.ContainsKey(SwaggerExtensions.ParameterGroupExtension)) @@ -24,49 +23,67 @@ public string Tag() return string.Empty; } - if (Parent.CodeModel.ShouldGenerateXmlSerialization) + if (!Parent.CodeModel.ShouldGenerateXmlSerialization) { - var sb = new StringBuilder("`xml:\""); + return string.Format("`json:\"{0}{1}\"`", SerializedName, omitEmpty ? ",omitempty" : ""); + } - bool hasParent = false; - if (Parent is CompositeTypeGo && !((CompositeTypeGo)Parent).IsWrapperType) - { - sb.Append(XmlName); - hasParent = true; - } + var sb = new StringBuilder("`xml:\""); - if (XmlIsWrapped) - { - if (hasParent) - { - sb.Append('>'); - } + bool hasParent = false; + if (Parent is CompositeTypeGo go && !go.IsWrapperType) + { + sb.Append(XmlName); + hasParent = true; + } - var asSequence = ModelType as SequenceTypeGo; - sb.Append(asSequence.ElementXmlName); - } - else if (XmlIsAttribute) + if (XmlIsWrapped) + { + if (hasParent) { - sb.Append(",attr"); + sb.Append('>'); } - sb.Append("\"`"); - return sb.ToString(); + var asSequence = ModelType as SequenceTypeGo; + sb.Append(asSequence.ElementXmlName); + } + else if (XmlIsAttribute) + { + sb.Append(",attr"); } - return string.Format("`json:\"{0},omitempty\"`", SerializedName); + sb.Append("\"`"); + return sb.ToString(); + } /// /// Returns true if this property represents custom metadata. /// - public bool IsMetadata + public bool IsMetadata => string.Compare(Name, "Metadata", StringComparison.OrdinalIgnoreCase) == 0 && ModelType is DictionaryTypeGo; + + /// + /// Determiens if this PropertyGo instance is equal to another. + /// + /// + /// + public override bool Equals(object value) { - get + if (value is PropertyGo goProperty) { - // unfortunately we have to use a heuristic - return string.Compare(Name, "Metadata", StringComparison.OrdinalIgnoreCase) == 0 && ModelType is DictionaryTypeGo; + return goProperty.Name == Name; } + + return false; + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer hash code. + public override int GetHashCode() + { + return Name.GetHashCode(); } } } diff --git a/src/Model/SequenceTypeGo.cs b/src/Model/SequenceTypeGo.cs index 1e408c39c..7923b63a9 100644 --- a/src/Model/SequenceTypeGo.cs +++ b/src/Model/SequenceTypeGo.cs @@ -8,9 +8,12 @@ namespace AutoRest.Go.Model { public class SequenceTypeGo : SequenceType { - public SequenceTypeGo() : base() + public SequenceTypeGo() { - Name.OnGet += v => $"[]{ElementType.Name}"; + Name.OnGet += v => + ElementType.HasInterface() ? + $"[]{ElementType.GetInterfaceName()}" : + $"[]{ElementType.Name}"; } /// diff --git a/src/TransformerGo.cs b/src/TransformerGo.cs index 803d5decc..e9de3083e 100644 --- a/src/TransformerGo.cs +++ b/src/TransformerGo.cs @@ -38,10 +38,28 @@ public override CodeModelGo TransformCodeModel(CodeModel cm) TransformModelTypes(cmg); TransformMethods(cmg); AzureExtensions.ProcessParameterizedHost(cmg); + TransformPropertyTypes(cmg); return cmg; } + private static void TransformPropertyTypes(CodeModelGo cmg) + { + foreach (var model in cmg.ModelTypes) + { + foreach (var property in model.Properties) + { + // Flattened fields are referred to with their type name, + // this name change generates correct custom unmarshalers and validation code, + // plus flattening does not need to be checked that often + if (property.ShouldBeFlattened() && property.ModelType is CompositeTypeGo) + { + property.Name = property.ModelType.HasInterface() ? property.ModelType.GetInterfaceName() : property.ModelType.Name.Value; + } + } + } + } + private void TransformEnumTypes(CodeModelGo cmg) { // fix up any enum types that are missing a name. From 2fe2c7a3e71e21b5b1cc4509778f56ba8b73a4b8 Mon Sep 17 00:00:00 2001 From: Vlad Barosan Date: Tue, 20 Mar 2018 17:49:23 -0700 Subject: [PATCH 03/10] Fix crash error --- src/Model/CompositeTypeGo.cs | 12 +++--------- src/Model/PropertyGo.cs | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Model/CompositeTypeGo.cs b/src/Model/CompositeTypeGo.cs index 604325f6e..1f32f7ca3 100644 --- a/src/Model/CompositeTypeGo.cs +++ b/src/Model/CompositeTypeGo.cs @@ -193,14 +193,9 @@ public void AddImports(HashSet imports) { Properties.ForEach(p => p.ModelType.AddImports(imports)); - if (NeedsXmlNameField) + if (IsPolymorphic || HasFlattenedFields || NeedsXmlNameField) { - imports.Add(PrimaryTypeGo.GetImportLine(package: "encoding/xml")); - } - - if (BaseIsPolymorphic && !IsPolymorphic) - { - imports.Add("\"encoding/json\""); + imports.Add($"\"encoding/{this.CodeModel.ToCodeModelGo().Encoding}\""); imports.Add("\"errors\""); } } @@ -216,7 +211,7 @@ public bool IsPolymorphicResponse() { if (BaseIsPolymorphic && BaseModelType != null) { - return (BaseModelType as CompositeTypeGo).IsPolymorphicResponse(); + return ((CompositeTypeGo) BaseModelType).IsPolymorphicResponse(); } return IsPolymorphic && IsResponseType; } @@ -259,7 +254,6 @@ public string Fields(bool forMarshaller) property.Name, enumType.Name, property.Tag()); - } else if (property.ModelType is DictionaryType) { diff --git a/src/Model/PropertyGo.cs b/src/Model/PropertyGo.cs index b10daf2b1..3855b7839 100644 --- a/src/Model/PropertyGo.cs +++ b/src/Model/PropertyGo.cs @@ -33,7 +33,7 @@ public string Tag(bool omitEmpty = true) bool hasParent = false; if (Parent is CompositeTypeGo go && !go.IsWrapperType) { - sb.Append(XmlName); + sb.Append(SerializedName); hasParent = true; } From c8528e017e22ce31cc3678e49b6fc3c413a139c0 Mon Sep 17 00:00:00 2001 From: Vlad Barosan Date: Wed, 21 Mar 2018 09:17:49 -0700 Subject: [PATCH 04/10] Port discriminator support and various fixes from v2 --- src/Model/CompositeTypeGo.cs | 2 +- src/Templates/EnumTemplate.cshtml | 52 ++- src/Templates/ModelTemplate.cshtml | 705 +++++++++++++++-------------- src/TransformerGo.cs | 136 ++++-- 4 files changed, 511 insertions(+), 384 deletions(-) diff --git a/src/Model/CompositeTypeGo.cs b/src/Model/CompositeTypeGo.cs index 1f32f7ca3..fca1a63a9 100644 --- a/src/Model/CompositeTypeGo.cs +++ b/src/Model/CompositeTypeGo.cs @@ -33,7 +33,7 @@ public class CompositeTypeGo : CompositeType public bool PreparerNeeded = false; - public string DiscriminatorEnumValue => DiscriminatorEnum.Values.FirstOrDefault(c => c.SerializedName.Equals(SerializedName)).Name; + public string DiscriminatorEnumValue => DiscriminatorEnum?.Values.FirstOrDefault(v => v.SerializedName.Equals(SerializedName))?.Name; public IEnumerable DerivedTypes => CodeModel.ModelTypes.Where(t => t.DerivesFrom(this)); diff --git a/src/Templates/EnumTemplate.cshtml b/src/Templates/EnumTemplate.cshtml index e0a9a44f8..88b95896b 100644 --- a/src/Templates/EnumTemplate.cshtml +++ b/src/Templates/EnumTemplate.cshtml @@ -1,4 +1,5 @@ -@using AutoRest.Go +@using System.Collections.Generic +@using AutoRest.Go @using AutoRest.Go.Model @using System.Linq @@ -10,24 +11,43 @@ @WrapComment("// ", Model.Documentation) type @Model.Name string + @EmptyLine -@if (values.Any()) +@if (Model.Values.Any()) { - -const ( - @foreach (var v in values) - { - var valName = EnumValueGo.FormatName(Model, v); - var desc = v.Description; - if (string.IsNullOrWhiteSpace(desc)) + var possibleFuncName = $"Possible{Model.Name}Values"; + var orderedValues = new List(); + + const ( + @foreach (var v in Model.Values.OrderBy(v => v.Name)) { - desc = string.Format("{0} ...", valName); + var comment = string.Empty; + var memName = CodeNamerGo.Instance.GetEnumMemberName(v.Name); + orderedValues.Add(memName); + + if (string.IsNullOrWhiteSpace(v.Description)) + { + comment = $"{CodeNamerGo.Instance.GetEnumMemberName(v.Name)} ..."; + + + // @comment + + } + else + { + comment = $"{CodeNamerGo.Instance.GetEnumMemberName(v.Name)} {v.Description}"; + + @WrapComment("// ", comment) + + } + + @(memName) @(Model.Name) = "@(v.SerializedName)" + + } + ) + // @possibleFuncName returns an array of possible values for the @Model.Name const type. + func @(possibleFuncName)() []@Model.Name { + return []@(Model.Name){@(string.Join(',', orderedValues))} } - - @WrapComment("// ", desc) - @(valName) @(Model.Name) = "@(v.SerializedName)" - } -) - } diff --git a/src/Templates/ModelTemplate.cshtml b/src/Templates/ModelTemplate.cshtml index 3f9575fc6..7cb9d292e 100644 --- a/src/Templates/ModelTemplate.cshtml +++ b/src/Templates/ModelTemplate.cshtml @@ -1,330 +1,375 @@ -@using AutoRest.Core.Model -@using AutoRest.Core.Utilities -@using AutoRest.Go -@using AutoRest.Go.Model -@using AutoRest.Go.Templates -@using System.Linq - -@inherits AutoRest.Core.Template -@{ - var shortName = Model.Name.ToString().ToShortName(); - var desc = $"{Model.Name} ..."; - if (!string.IsNullOrEmpty(Model.Documentation)) - { - desc = $"{Model.Name} - {Model.Documentation}"; - } -} - -@if (Model.IsPolymorphic) -{ - -type @(Model.Name) interface { - @foreach (var dt in Model.DerivedTypes) - { - @:As@(dt.Name) () (*@(dt.Name), bool) - } -} - -@EmptyLine -func unmarshal@(Model.Name)(body []byte) (@(Model.Name), error){ - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } -@EmptyLine - switch m["@(Model.PolymorphicDiscriminator)"] { - @foreach (var dt in Model.DerivedTypes) - { - - case string(@((dt as CompositeTypeGo).DiscriminatorEnumValue)): - var @(dt.Name.FixedValue.ToShortName()) @(dt.Name) - err := json.Unmarshal(body, &@(dt.Name.FixedValue.ToShortName())) - return @(dt.Name.FixedValue.ToShortName()), err - - } - default: - return nil, errors.New("Unsupported type") - } -} - -func unmarshal@(Model.Name)Array(body []byte) ([]@(Model.Name), error){ - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } -@EmptyLine - @(Model.Name.FixedValue.ToShortName())Array := make([]@(Model.Name), len(rawMessages)) -@EmptyLine - for index, rawMessage := range rawMessages { - @(Model.Name.FixedValue.ToShortName()), err := unmarshal@(Model.Name)(*rawMessage) - if err != nil { - return nil, err - } - @(Model.Name.FixedValue.ToShortName())Array[index] = @(Model.Name.FixedValue.ToShortName()) - } - return @(Model.Name.FixedValue.ToShortName())Array, nil -} - -} -else -{ - -@WrapComment("// ", desc) -type @Model.Name struct { - @if (Model.IsResponseType) - { - @:rawResponse *http.Response - } -@(Model.Fields(false)) -} - -} - -@if (Model.BaseIsPolymorphic && !Model.IsPolymorphic) -{ - -@EmptyLine -// MarshalJSON is the custom marshaler for @(Model.Name). -func (@(Model.Name.FixedValue.ToShortName()) @(Model.Name))MarshalJSON() ([]byte, error){ - @(Model.Name.FixedValue.ToShortName()).@(Model.PolymorphicProperty) = @(Model.DiscriminatorEnumValue) - type Alias @(Model.Name) - return json.Marshal(&struct { - Alias - }{ - Alias: (Alias)(@(Model.Name.FixedValue.ToShortName())), - }) -} - - @foreach (var st in Model.SiblingTypes) - { - -@EmptyLine -// As@(st.Name) is the @(Model.BaseModelType.Name) implementation for @(Model.Name). -func (@(Model.Name.FixedValue.ToShortName()) @(Model.Name)) As@(st.Name)() (*@(st.Name), bool) { - @if (st.Equals(Model)) - { - @:return &@(Model.Name.FixedValue.ToShortName()), true - } - else - { - @:return nil, false - } -} - - } - -} - -@if (Model.HasPolymorphicFields && !Model.IsPolymorphic) -{ - -// UnmarshalJSON is the custom unmarshaler for @(Model.Name) struct. -func (@(Model.Name.FixedValue.ToShortName()) *@(Model.Name)) UnmarshalJSON(body []byte) error { - @if (Model.IsWrapperType) - { - - @(Model.BaseType.Name.FixedValue.ToShortName()), err := unmarshal@(Model.BaseType.Name)(body) - if err != nil { - return err - } - @(Model.Name.FixedValue.ToShortName()).Value = @(Model.BaseType.Name.FixedValue.ToShortName()) - - } - else - { - - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - - var v *json.RawMessage - @foreach (var p in Model.AllProperties) - { - - @EmptyLine - v = m["@(p.SerializedName)"] - if v != nil { - @if (p.ModelType is CompositeType && (p.ModelType as CompositeTypeGo).IsPolymorphic) - { - @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(p.ModelType.Name)(*m["@(p.SerializedName)"]) - } - else if (p.ModelType is SequenceType && (p.ModelType as SequenceTypeGo).ElementType is CompositeType && ((p.ModelType as SequenceTypeGo).ElementType as CompositeType).IsPolymorphic) - { - @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@((p.ModelType as SequenceTypeGo).ElementType.Name)Array(*m["@(p.SerializedName)"]) - } - else - { - - var @(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) @(p.ModelType.Name) - err = json.Unmarshal(*m["@(p.SerializedName)"], &@(CodeNamerGo.Instance.GetVariableName(p.SerializedName))) - - } - if err != nil { - return err - } - @if ((p.ModelType is CompositeType && (p.ModelType as CompositeTypeGo).IsPolymorphic) || (p.ModelType is EnumType)) - { - @:@(Model.Name.FixedValue.ToShortName()).@(p.Name) = @(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) - } - else - { - @:@(Model.Name.FixedValue.ToShortName()).@(p.Name) = &@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) - } - } - - } - - } -@EmptyLine - return nil -} - -} - -@EmptyLine -@if (Model.IsResponseType) -{ - var statusCodeFn = "StatusCode"; - var statusFn = "Status"; - if (Model.Properties.Where(p => p.Name == statusCodeFn).Any()) - { - statusCodeFn = $"HTTP{statusCodeFn}"; - } - if (Model.Properties.Where(p => p.Name == statusFn).Any()) - { - statusFn = $"HTTP{statusFn}"; - } - -// Response returns the raw HTTP response object. -func (@shortName @Model.Name) Response() *http.Response { - return @(shortName).rawResponse -} - -// @statusCodeFn returns the HTTP status code of the response, e.g. 200. -func (@shortName @Model.Name) @(statusCodeFn)() int { - return @(shortName).rawResponse.StatusCode -} - -// @statusFn returns the HTTP status message of the response, e.g. "200 OK". -func (@shortName @Model.Name) @(statusFn)() string { - return @(shortName).rawResponse.Status -} - - if (Model.IsStreamType()) - { - - -// Body returns the raw HTTP response object's Body. -func (@shortName @Model.Name) Body() io.ReadCloser { - return @(shortName).rawResponse.Body -} - - } -} -@EmptyLine -@foreach (var headerResp in Model.ResponseHeaders()) -{ - var retSig = "string"; - var retExpr = $"{shortName}.rawResponse.Header.Get(\"{headerResp.Type.SerializedName}\")"; - if (headerResp.Type.ModelType.IsDateTimeType()) - { - retSig = "time.Time"; - } - else if (headerResp.Type.ModelType is EnumTypeGo) - { - retSig = headerResp.Type.ModelTypeName; - retExpr = $"{headerResp.Type.ModelType.Name}({retExpr})"; - } - else if (headerResp.Type.ModelType.IsETagType()) - { - retSig = CodeNamerGo.Instance.ETagTypeName; - retExpr = $"{CodeNamerGo.Instance.ETagTypeName}({retExpr})"; - } - else if (headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Int) || headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Long)) - { - retSig = headerResp.Type.ModelTypeName; - } - else if (headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.ByteArray)) - { - retSig = "[]byte"; - } - -// @headerResp.Name returns the value for header @(headerResp.Type.SerializedName). -func (@shortName @Model.Name) @(headerResp.Name)() @retSig { - - if (headerResp.Type.ModelType.IsDateTimeType()) - { - var formatAs = "rfc3339Format"; - if (headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.DateTimeRfc1123)) - { - formatAs = "time.RFC1123"; - } - - s := @retExpr - if s == "" { - return time.Time{} - } - t, err := time.Parse(@formatAs, s) - if err != nil { - panic(err) - } - return t - - } - else if (headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Int) || headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Long)) - { - var bitSize = headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Int) ? "32" : "64"; - - s := @retExpr - if s == "" { - return -1 - } - i, err := strconv.ParseInt(s, 10, @bitSize) - if err != nil { - panic(err) - } - return @(headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Int) ? "int32(i)" : "i") - - } - else if (headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.ByteArray)) - { - - s := @retExpr - if s == "" { - return nil - } - b, err := base64.StdEncoding.DecodeString(s) - if err != nil { - panic(err) - } - return b - - } - else - { - @:return @retExpr - } -@:} -} - -@if (Model.ResponseIncludesMetadata) -{ - -// NewMetadata returns user-defined key/value pairs. -func(@shortName @Model.Name) NewMetadata() Metadata { - md := Metadata{} - for k, v := range @(shortName).rawResponse.Header { - if len(k) > mdPrefixLen { - if prefix := k[0:mdPrefixLen]; strings.EqualFold(prefix, mdPrefix) { - md[strings.ToLower(k[mdPrefixLen:])] = v[0] - } - } - } - return md -} - -} +@using AutoRest.Core.Model +@using AutoRest.Core.Utilities +@using AutoRest.Go +@using AutoRest.Go.Model +@using AutoRest.Go.Templates +@using System.Linq +@inherits AutoRest.Core.Template +@{ + var shortName = Model.Name.ToString().ToShortName(); + var desc = $"{Model.Name} ..."; + if (!string.IsNullOrEmpty(Model.Documentation)) + { + desc = $"{Model.Name} - {Model.Documentation}"; + } +} +@if (Model.HasInterface()) +{ + @WrapComment("// ", $"{Model.GetInterfaceName()} {Model.Documentation.ToSentence()}") + + type @(Model.GetInterfaceName()) interface { + @foreach (var dt in Model.DerivedTypes) + { + @:As@(dt.Name) () (*@(dt.Name), bool) + if (dt.HasInterface()) + { + @:As@(dt.GetInterfaceName()) () (@(dt.GetInterfaceName()), bool) + } + } + As@(Model.Name) () (*@(Model.Name), bool) + } + + @EmptyLine + @WrapComment("// ", $"{Model.Name} {Model.Documentation.ToSentence()}") + type @(Model.Name) struct { + @(Model.AddHTTPResponse()) + @(Model.Fields(forMarshaller: false)) + } + + @EmptyLine + func unmarshal@(Model.GetInterfaceName())(body []byte) (@(Model.GetInterfaceName()), error){ + var m map[string]interface{} + err := json.Unmarshal(body, &m) + if err != nil { + return nil, err + } + @EmptyLine + switch m["@(Model.RootType.PolymorphicDiscriminator)"] { + @foreach (var dt in Model.DerivedTypes) + { + + case string(@(CodeNamerGo.Instance.GetEnumMemberName((dt as CompositeTypeGo).DiscriminatorEnumValue))): + var @(dt.Name.FixedValue.ToShortName()) @(dt.Name) + err := json.Unmarshal(body, &@(dt.Name.FixedValue.ToShortName())) + return @(dt.Name.FixedValue.ToShortName()), err + + } + default: + var @(Model.Name.FixedValue.ToShortName()) @(Model.Name) + err := json.Unmarshal(body, &@(Model.Name.FixedValue.ToShortName())) + return @(Model.Name.FixedValue.ToShortName()), err + } + } + + func unmarshal@(Model.GetInterfaceName())Array(body []byte) ([]@(Model.GetInterfaceName()), error){ + var rawMessages []*json.RawMessage + err := json.Unmarshal(body, &rawMessages) + if err != nil { + return nil, err + } + @EmptyLine + @(Model.Name.FixedValue.ToShortName())Array := make([]@(Model.GetInterfaceName()), len(rawMessages)) + @EmptyLine + for index, rawMessage := range rawMessages { + @(Model.Name.FixedValue.ToShortName()), err := unmarshal@(Model.GetInterfaceName())(*rawMessage) + if err != nil { + return nil, err + } + @(Model.Name.FixedValue.ToShortName())Array[index] = @(Model.Name.FixedValue.ToShortName()) + } + return @(Model.Name.FixedValue.ToShortName())Array, nil + } + +} +else +{ + + @EmptyLine + @WrapComment("// ", desc) + type @Model.Name struct { + @if (Model.IsResponseType) + { + @:rawResponse *http.Response + } + @(Model.Fields(false)) + } + +} + +@if (Model.BaseIsPolymorphic && Model.IsPolymorphic) +{ + + @EmptyLine + // MarshalJSON is the custom marshaler for @(Model.Name). + func (@(Model.Name.FixedValue.ToShortName()) @(Model.Name))MarshalJSON() ([]byte, error){ + @(Model.Name.FixedValue.ToShortName()).@(Model.PolymorphicProperty) = @(Model.DiscriminatorEnumValue) + type Alias @(Model.Name) + return json.Marshal(&struct { + Alias + }{ + Alias: (Alias)(@(Model.Name.FixedValue.ToShortName())), + }) + } + + @foreach (var st in Model.SiblingTypes) + { + + @EmptyLine + // As@(st.Name) is the @(Model.RootType.GetInterfaceName()) implementation for @(Model.Name). + func (@(Model.Name.FixedValue.ToShortName()) @(Model.Name)) As@(st.Name)() (*@(st.Name), bool) { + @if (st.Equals(Model)) + { + @:return &@(Model.Name.FixedValue.ToShortName()), true + } + else + { + @:return nil, false + } + } + @if (st.HasInterface()) + { + @EmptyLine + @:// As@(st.GetInterfaceName()) is the @(Model.RootType.GetInterfaceName()) implementation for @(Model.Name). + @:func(@(Model.Name.FixedValue.ToShortName()) @(Model.Name)) As@(st.GetInterfaceName())()(@(st.GetInterfaceName()), bool) { + if (st.Equals(Model) || Model.DerivesFrom(st)) + { + @:return &@(Model.Name.FixedValue.ToShortName()), true + } + else + { + @:return nil, false + } + @:} + @EmptyLine + } + +} + +} + +@if (Model.HasPolymorphicFields && Model.HasFlattenedFields) +{ + + // UnmarshalJSON is the custom unmarshaler for @(Model.Name) struct. + func (@(Model.Name.FixedValue.ToShortName()) *@(Model.Name)) UnmarshalJSON(body []byte) error { + @if (Model.IsWrapperType) + { + if (Model.BaseType is SequenceTypeGo sequenceType) + { + @:@(sequenceType.ElementType.Name.FixedValue.ToShortName()), err := unmarshal@(sequenceType.ElementType.GetInterfaceName())Array(body) + } + else + { + @:@(Model.BaseType.Name.FixedValue.ToShortName()), err := unmarshal@(Model.BaseType.GetInterfaceName())(body) + } + + if err != nil { + return err + } + + if (Model.BaseType is SequenceType type) + { + @:@(Model.Name.FixedValue.ToShortName()).Value = &@type.ElementType.Name.FixedValue.ToShortName() + } + else + { + @:@(Model.Name.FixedValue.ToShortName()).Value = @(Model.BaseType.Name.FixedValue.ToShortName()) + } + } + else + { + + var m map[string]*json.RawMessage + err := json.Unmarshal(body, &m) + if err != nil { + return err + } + + var v *json.RawMessage + @foreach (var p in Model.AllProperties) + { + + @EmptyLine + v = m["@(p.SerializedName)"] + if v != nil { + @if (p.ModelType.HasInterface()) + { + @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(p.ModelType.GetInterfaceName())(*m["@(p.SerializedName)"]) + } + else if (p.ModelType is SequenceTypeGo sequenceType && sequenceType.ElementType.HasInterface()) + { + @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(sequenceType.ElementType.GetInterfaceName())Array(*m["@(p.SerializedName)"]) + } + else if (p.ModelType is CompositeType && ((CompositeTypeGo) p.ModelType).IsPolymorphic) + { + @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(p.ModelType.Name)(*m["@(p.SerializedName)"]) + } + else + { + + var @(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) @(p.ModelType.Name) + err = json.Unmarshal(*m["@(p.SerializedName)"], &@(CodeNamerGo.Instance.GetVariableName(p.SerializedName))) + + } + if err != nil { + return err + } + @if (p.ModelType.HasInterface() || p.ModelType is EnumType) + { + @:@(Model.Name.FixedValue.ToShortName()).@(p.Name) = @(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) + } + else + { + @:@(Model.Name.FixedValue.ToShortName()).@(p.Name) = &@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) + } + } + + } + + } + @EmptyLine + return nil + } + +} +@EmptyLine +@if (Model.IsResponseType) +{ + var statusCodeFn = "StatusCode"; + var statusFn = "Status"; + if (Model.Properties.Any(p => p.Name == statusCodeFn)) + { + statusCodeFn = $"HTTP{statusCodeFn}"; + } + if (Model.Properties.Any(p => p.Name == statusFn)) + { + statusFn = $"HTTP{statusFn}"; + } + + // Response returns the raw HTTP response object. + func (@shortName @Model.Name) Response() *http.Response { + return @(shortName).rawResponse + } + + // @statusCodeFn returns the HTTP status code of the response, e.g. 200. + func (@shortName @Model.Name) @(statusCodeFn)() int { + return @(shortName).rawResponse.StatusCode + } + + // @statusFn returns the HTTP status message of the response, e.g. "200 OK". + func (@shortName @Model.Name) @(statusFn)() string { + return @(shortName).rawResponse.Status + } + + if (Model.IsStreamType()) + { + + // Body returns the raw HTTP response object's Body. + func (@shortName @Model.Name) Body() io.ReadCloser { + return @(shortName).rawResponse.Body + } + + } +} +@EmptyLine +@foreach (var headerResp in Model.ResponseHeaders()) +{ + var retSig = "string"; + var retExpr = $"{shortName}.rawResponse.Header.Get(\"{headerResp.Type.SerializedName}\")"; + if (headerResp.Type.ModelType.IsDateTimeType()) + { + retSig = "time.Time"; + } + else if (headerResp.Type.ModelType is EnumTypeGo) + { + retSig = headerResp.Type.ModelTypeName; + retExpr = $"{headerResp.Type.ModelType.Name}({retExpr})"; + } + else if (headerResp.Type.ModelType.IsETagType()) + { + retSig = CodeNamerGo.Instance.ETagTypeName; + retExpr = $"{CodeNamerGo.Instance.ETagTypeName}({retExpr})"; + } + else if (headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Int) || headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Long)) + { + retSig = headerResp.Type.ModelTypeName; + } + else if (headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.ByteArray)) + { + retSig = "[]byte"; + } + + // @headerResp.Name returns the value for header @(headerResp.Type.SerializedName). + func (@shortName @Model.Name) @(headerResp.Name)() @retSig { + + if (headerResp.Type.ModelType.IsDateTimeType()) + { + var formatAs = "rfc3339Format"; + if (headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.DateTimeRfc1123)) + { + formatAs = "time.RFC1123"; + } + + s := @retExpr + if s == "" { + return time.Time{} + } + t, err := time.Parse(@formatAs, s) + if err != nil { + panic(err) + } + return t + + } + else if (headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Int) || headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Long)) + { + var bitSize = headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Int) ? "32" : "64"; + + s := @retExpr + if s == "" { + return -1 + } + i, err := strconv.ParseInt(s, 10, @bitSize) + if err != nil { + panic(err) + } + return @(headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.Int) ? "int32(i)" : "i") + + } + else if (headerResp.Type.ModelType.IsPrimaryType(KnownPrimaryType.ByteArray)) + { + + s := @retExpr + if s == "" { + return nil + } + b, err := base64.StdEncoding.DecodeString(s) + if err != nil { + panic(err) + } + return b + + } + else + { + @:return @retExpr + } + @:} +} +@if (Model.ResponseIncludesMetadata) +{ + + // NewMetadata returns user-defined key/value pairs. + func(@shortName @Model.Name) NewMetadata() Metadata { + md := Metadata{} + for k, v := range @(shortName).rawResponse.Header { + if len(k) > mdPrefixLen { + if prefix := k[0:mdPrefixLen]; strings.EqualFold(prefix, mdPrefix) { + md[strings.ToLower(k[mdPrefixLen:])] = v[0] + } + } + } + return md + } + +} diff --git a/src/TransformerGo.cs b/src/TransformerGo.cs index e9de3083e..1e7229c44 100644 --- a/src/TransformerGo.cs +++ b/src/TransformerGo.cs @@ -33,11 +33,12 @@ public override CodeModelGo TransformCodeModel(CodeModel cm) SwaggerExtensions.ProcessGlobalParameters(cmg); // Add the current package name as a reserved keyword CodeNamerGo.Instance.ReserveNamespace(cm.Namespace); - FixStutteringTypeNames(cmg); TransformEnumTypes(cmg); TransformModelTypes(cmg); TransformMethods(cmg); AzureExtensions.ProcessParameterizedHost(cmg); + FixStutteringTypeNames(cmg); + AssureUniqueNames(cmg); TransformPropertyTypes(cmg); return cmg; @@ -105,55 +106,116 @@ private void TransformEnumTypes(CodeModelGo cmg) } // Add discriminators - foreach (var mt in cmg.ModelTypes) + // For all polymorphic types we need an enum with values for all the implementing types. + // To do this: + // 1. Create an enum with the basic type and all derived types as values. + // 2. Check if there is any enum already present that has the same name. If there is, check if it contains all the values of current enum. + // 3. If it has the same name and all values of current enum, use it. Otherwise create a new one and use it. + foreach (var mt in cmg.ModelTypes.Cast()) { - if (mt.IsPolymorphic) + if (!mt.IsPolymorphic) { - var values = new List(); - foreach (var dt in (mt as CompositeTypeGo).DerivedTypes) - { - var ev = new EnumValueGo(); - ev.Name = string.Format("{0}{1}", CodeNamerGo.Instance.GetTypeName(mt.PolymorphicDiscriminator), - CodeNamerGo.Instance.GetTypeName(dt.SerializedName)); - ev.SerializedName = dt.SerializedName; - values.Add(ev); - } - bool nameAlreadyExists = cmg.EnumTypes.Any(et => et.Name.EqualsIgnoreCase(mt.PolymorphicDiscriminator)); - bool alreadyExists = nameAlreadyExists; - if (nameAlreadyExists) + continue; + } + + var enumValues = new List(); + + var baseTypeEnumValue = new EnumValue + { + Name = $"{CodeNamerGo.Instance.GetTypeName(mt.PolymorphicDiscriminator)}{CodeNamerGo.Instance.GetTypeName(mt.SerializedName)}", + SerializedName = mt.SerializedName + }; + + enumValues.Add(baseTypeEnumValue); + + foreach (var dt in mt.DerivedTypes) + { + var ev = new EnumValue { - (mt as CompositeTypeGo).DiscriminatorEnum = cmg.EnumTypes.First(et => et.Name.EqualsIgnoreCase(mt.PolymorphicDiscriminator)); - var existingValues = new List(); - foreach (var v in cmg.EnumTypes.First(et => et.Name.EqualsIgnoreCase(mt.PolymorphicDiscriminator)).Values) - { - existingValues.Add(v.SerializedName); - } - foreach (var v in values) - { - if (!existingValues.Any(ev => ev.Equals(v.SerializedName))) - { - alreadyExists = false; - } - } - } - if (!alreadyExists) + Name = $"{CodeNamerGo.Instance.GetTypeName(mt.PolymorphicDiscriminator)}{CodeNamerGo.Instance.GetTypeName(dt.SerializedName)}", + SerializedName = dt.SerializedName + }; + enumValues.Add(ev); + } + + var enumAlreadyExists = false; + var enumWithSameName = (EnumTypeGo)cmg.EnumTypes.FirstOrDefault(et => et.Name.EqualsIgnoreCase(CodeNamerGo.Instance.GetTypeName(mt.PolymorphicDiscriminator))); + + if (enumWithSameName != null) + { + enumAlreadyExists = !enumValues.Select(value => value.SerializedName).Except(enumWithSameName.Values.Select(value => value.SerializedName)).Any(); + } + + if (enumAlreadyExists) + { + mt.DiscriminatorEnum = enumWithSameName; + } + else + { + mt.DiscriminatorEnum = cmg.Add(New(new { - (mt as CompositeTypeGo).DiscriminatorEnum = cmg.Add(New(new{ - Name = nameAlreadyExists ? string.Format("{0}{1}", mt.PolymorphicDiscriminator, mt.Name) : mt.PolymorphicDiscriminator, - Values = values, - })); - } + Name = enumWithSameName == null ? mt.PolymorphicDiscriminator : $"{mt.PolymorphicDiscriminator}{mt.GetInterfaceName()}", + Values = enumValues, + })) as EnumTypeGo; } } + } + private static void AssureUniqueNames(CodeModelGo cmg) + { // now normalize the names // NOTE: this must be done after all enum types have been accounted for foreach (var enumType in cmg.EnumTypes) { - enumType.SetName(CodeNamer.Instance.GetTypeName(enumType.Name.FixedValue)); + enumType.SetName(CodeNamerGo.Instance.GetTypeName(enumType.Name.FixedValue)); foreach (var v in enumType.Values) { - v.Name = CodeNamer.Instance.GetEnumMemberName(v.Name); + v.Name = CodeNamerGo.Instance.GetEnumMemberName(v.Name); + } + } + + // Ensure all enumerated type values have the simplest possible unique names + // -- The code assumes that all public type names are unique within the client and that the values + // of an enumerated type are unique within that type. To safely promote the enumerated value name + // to the top-level, it must not conflict with other existing types. If it does, prepending the + // value name with the (assumed to be unique) enumerated type name will make it unique. + + // First, collect all type names (since these cannot change) + var topLevelNames = new HashSet(); + foreach (var mt in cmg.ModelTypes) + { + topLevelNames.Add(mt.Name); + } + + foreach (var em in cmg.EnumTypes) + { + topLevelNames.Add(em.Name); + } + + // Then, note each enumerated type with one or more conflicting values and collect the values from + // those enumerated types without conflicts. do this on a sorted list to ensure consistent naming + foreach (var em in cmg.EnumTypes.Cast().OrderBy(etg => etg.Name.Value)) + { + if (em.Values.Any(v => topLevelNames.Contains(v.Name) || CodeNamerGo.Instance.UserDefinedNames.Contains(v.Name))) + { + foreach (var v in em.Values) + { + v.Name = em.Name + v.Name; + } + } + else + { + topLevelNames.UnionWith(em.Values.Select(ev => ev.Name)); + } + } + + var modelList = new List(); + foreach (var em in cmg.EnumTypes.OrderBy(etg => etg.Name.Value)) + { + foreach (var v in em.Values) + { + v.Name = CodeNamerGo.Instance.GetUnique(v.Name, v, cmg.ModelTypes, modelList); + modelList.Add(v); } } } From e22d779f12aa38182b795d82879fa4e3b3b8ca24 Mon Sep 17 00:00:00 2001 From: Vlad Barosan Date: Thu, 22 Mar 2018 15:42:45 -0700 Subject: [PATCH 05/10] Implement xml unmarshalling --- src/CodeGeneratorGo.cs | 8 - src/CodeNamerGo.cs | 14 +- src/Model/CodeModelGo.cs | 10 +- src/Model/CompositeTypeGo.cs | 36 ++-- src/Model/ParameterGo.cs | 20 +-- src/Model/PropertyGo.cs | 6 +- src/Templates/Marshalling.cshtml | 34 +--- src/Templates/MarshallingJson.cshtml | 160 +++++++++++++++++ src/Templates/MarshallingXml.cshtml | 137 ++++++++++++++ src/Templates/ModelTemplate.cshtml | 241 +++++++------------------ src/Templates/ModelsTemplate.cshtml | 258 ++++++++++++++------------- src/autorest.go.csproj | 8 +- 12 files changed, 544 insertions(+), 388 deletions(-) create mode 100644 src/Templates/MarshallingJson.cshtml create mode 100644 src/Templates/MarshallingXml.cshtml diff --git a/src/CodeGeneratorGo.cs b/src/CodeGeneratorGo.cs index 3ad214a8c..b0fcdbf70 100644 --- a/src/CodeGeneratorGo.cs +++ b/src/CodeGeneratorGo.cs @@ -17,7 +17,6 @@ public class CodeGeneratorGo : CodeGenerator private const string ModelsFileName = "models"; private const string ClientFileName = "client"; private const string VersionFileName = "version"; - private const string MarshallingFileName = "marshalling"; private const string ResponderFileName = "responder_policy"; private const string ResponseErrorFileName = "response_error"; private const string ValidationFileName = "validation"; @@ -33,7 +32,6 @@ public CodeGeneratorGo() ModelsFileName, ClientFileName, VersionFileName, - MarshallingFileName, ResponderFileName, ResponseErrorFileName, ValidationFileName @@ -101,12 +99,6 @@ public override async Task Generate(CodeModel cm) { await Write(template.Item1, FormatFileName(template.Item2)); } - - if (codeModel.RequiresMarshallers.Any()) - { - var marshallingTemplate = new Marshalling { Model = codeModel }; - await Write(marshallingTemplate, FormatFileName(MarshallingFileName)); - } } /// diff --git a/src/CodeNamerGo.cs b/src/CodeNamerGo.cs index 0df83e6ac..32ecb93fb 100644 --- a/src/CodeNamerGo.cs +++ b/src/CodeNamerGo.cs @@ -33,7 +33,7 @@ public virtual IEnumerable StandardImports } } - public virtual IEnumerable PageableImports => new string[] + public virtual IEnumerable PageableImports => new string[] { PrimaryTypeGo.GetImportLine(package: "net/http") }; @@ -131,8 +131,8 @@ public CodeNamerGo() "case", "defer", "go", "map", "struct", "chan", "else", "goto", "package", "switch", "const", "fallthrough", "if", "range", "type", - "continue", "for", "import", "return", "var", - + "continue", "for", "import", "return", "var", + // Reserved predeclared identifiers -- list retrieved from http://golang.org/ref/spec#Predeclared_identifiers "bool", "byte", "complex64", "complex128", @@ -193,7 +193,7 @@ public CodeNamerGo() "unsafe", // Other reserved names and packages (defined by the base libraries this code uses) - "autorest", "client", "date", "err", "req", "resp", "result", "sender", "to", "validation" + "autorest", "client", "date", "err", "req", "resp", "result", "sender", "to", "validation", "e", "d", "start" }); } @@ -287,7 +287,7 @@ public override string GetParameterName(string name) /// /// /// The formatted string. - public override string GetPropertyName(string name) => + public override string GetPropertyName(string name) => string.IsNullOrWhiteSpace(name) ? name : EnsureNameCase(GetEscapedReservedName(RemoveInvalidCharacters(PascalCase(name)), "Property")); @@ -297,7 +297,7 @@ public override string GetPropertyName(string name) => /// /// /// The formatted string. - public override string GetTypeName(string name) => + public override string GetTypeName(string name) => string.IsNullOrWhiteSpace(name) ? name : EnsureNameCase(GetEscapedReservedName(RemoveInvalidCharacters(PascalCase(name)), "Type")); @@ -307,7 +307,7 @@ public override string GetTypeName(string name) => /// /// /// The formatted string. - public override string GetVariableName(string name) => + public override string GetVariableName(string name) => string.IsNullOrWhiteSpace(name) ? name : EnsureNameCase(GetEscapedReservedName(CamelCase(RemoveInvalidCharacters(name)), "Var")); diff --git a/src/Model/CodeModelGo.cs b/src/Model/CodeModelGo.cs index e1e8ae915..4076b5107 100644 --- a/src/Model/CodeModelGo.cs +++ b/src/Model/CodeModelGo.cs @@ -141,7 +141,7 @@ public string GlobalParameters { declarations.Add( string.Format( - (p.IsRequired || p.ModelType.CanBeNull() ? "{0} {1}" : "{0} *{1}"), + ((PropertyGo)p).IsPointer ? "{0} *{1}" : "{0} {1}", p.Name.Value.ToSentence(), p.ModelType.Name)); } } @@ -176,7 +176,7 @@ public string GlobalDefaultParameters { declarations.Add( string.Format( - (p.IsRequired || p.ModelType.CanBeNull() ? "{0} {1}" : "{0} *{1}"), + (((PropertyGo)p).IsPointer ? "{0} *{1}" : "{0} {1}"), p.Name.Value.ToSentence(), p.ModelType.Name.Value.ToSentence())); } } @@ -298,10 +298,10 @@ public static string FormatVersion(string version) public bool UsesETags => ModelTypes.Any(m => m.Properties.Any(p => p.ModelType.IsETagType())); /// - /// Returns a collection of composite types that require custom marshalling and/or - /// unmarshalling. Can be empty if there are no types requriring marshallers. + /// Returns a collection of composite types that require datetime handleing in a custom marshaller and/or + /// unmarshaller. Can be empty if there are no types requriring marshallers. /// - public IEnumerable RequiresMarshallers => ModelTypes.Cast().Where(m => m.Properties.Any(p => p.ModelType.IsDateTimeType())); + public IEnumerable RequiresDateTimeCustomHandling=> ModelTypes.Cast().Where(m => m.IsDateTimeCustomHandlingRequired); /// /// Returns the encoding type used for serialization (e.g. xml or json). diff --git a/src/Model/CompositeTypeGo.cs b/src/Model/CompositeTypeGo.cs index fca1a63a9..85ebe725d 100644 --- a/src/Model/CompositeTypeGo.cs +++ b/src/Model/CompositeTypeGo.cs @@ -52,18 +52,12 @@ public IEnumerable SiblingTypes } } - public bool HasPolymorphicFields - { - get - { - return AllProperties.Any(p => - // polymorphic composite - p.ModelType.HasInterface() || - // polymorphic array - (p.ModelType is SequenceType sequenceType && - sequenceType.ElementType.HasInterface())); - } - } + public bool HasPolymorphicFields => + AllProperties.Any(p => + // polymorphic composite + p.ModelType.HasInterface() || + // polymorphic array + (p.ModelType is SequenceType sequenceType && sequenceType.ElementType.HasInterface())); public CompositeTypeGo() { @@ -137,8 +131,8 @@ internal void AddPolymorphicPropertyIfNecessary() /// public bool HasFlattenedFields => Properties.Any(p => p.ModelType is CompositeTypeGo && p.ShouldBeFlattened()); - public string PolymorphicProperty => !string.IsNullOrEmpty(PolymorphicDiscriminator) - ? CodeNamerGo.Instance.GetPropertyName(PolymorphicDiscriminator) + public string PolymorphicProperty => !string.IsNullOrEmpty(PolymorphicDiscriminator) + ? CodeNamerGo.Instance.GetPropertyName(PolymorphicDiscriminator) : ((CompositeTypeGo) BaseModelType).PolymorphicProperty; public IEnumerable AllProperties => BaseModelType != null ? @@ -196,7 +190,13 @@ public void AddImports(HashSet imports) if (IsPolymorphic || HasFlattenedFields || NeedsXmlNameField) { imports.Add($"\"encoding/{this.CodeModel.ToCodeModelGo().Encoding}\""); - imports.Add("\"errors\""); + } + + if (IsDateTimeCustomHandlingRequired) + { + imports.Add($"\"reflect\""); + imports.Add($"\"time\""); + imports.Add($"\"unsafe\""); } } @@ -293,7 +293,7 @@ public string Fields(bool forMarshaller) property.Name = NextLink; } - var deref = property.ModelType.CanBeNull() || property.IsRequired ? string.Empty : "*"; + var deref = property.IsPointer ? "*" : string.Empty; var typeName = property.ModelType.Name.ToString(); if (forMarshaller && property.ModelType.IsDateTimeType()) { @@ -388,7 +388,7 @@ public IEnumerable ResponseHeaders() } } } - respHeaders.Sort((lhs, rhs) => { return string.Compare(lhs.Name, rhs.Name, StringComparison.OrdinalIgnoreCase); }); + respHeaders.Sort((lhs, rhs) => string.Compare(lhs.Name, rhs.Name, StringComparison.OrdinalIgnoreCase)); return respHeaders; } @@ -420,5 +420,7 @@ public bool ResponseIncludesMetadata return false; } } + + internal bool IsDateTimeCustomHandlingRequired => Properties.Any(p => p.ModelType.IsDateTimeType()); } } diff --git a/src/Model/ParameterGo.cs b/src/Model/ParameterGo.cs index bf1377e74..e71c06226 100644 --- a/src/Model/ParameterGo.cs +++ b/src/Model/ParameterGo.cs @@ -254,12 +254,9 @@ public override IModelType ModelType return base.ModelType; } // this is a header collection so emit it as a map[string]string - return new DictionaryTypeGo() { ValueType = base.ModelType, CodeModel = base.ModelType.CodeModel, SupportsAdditionalProperties = false }; - } - set - { - base.ModelType = value; + return new DictionaryTypeGo { ValueType = base.ModelType, CodeModel = base.ModelType.CodeModel, SupportsAdditionalProperties = false }; } + set => base.ModelType = value; } /// @@ -273,17 +270,12 @@ public string GetOptionalComparand() throw new Exception($"GetOptionalComparand called on required paramater {Name}"); } - if (ModelType is EnumTypeGo) + if (!(ModelType is EnumTypeGo et)) { - var et = ModelType as EnumTypeGo; - var typeName = et.Name.ToString(); - if (typeName.EndsWith("Type")) - { - typeName = typeName.Substring(0, typeName.Length - 4); - } - return $"{typeName}None"; + return "nil"; } - return "nil"; + + return $"{et.Name}None"; } /// diff --git a/src/Model/PropertyGo.cs b/src/Model/PropertyGo.cs index 3855b7839..4f2c3df1c 100644 --- a/src/Model/PropertyGo.cs +++ b/src/Model/PropertyGo.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using AutoRest.Core.Utilities; using AutoRest.Core.Model; using AutoRest.Extensions; using System; @@ -57,6 +56,11 @@ public string Tag(bool omitEmpty = true) } + /// + /// Gets if the property should be treated as a pointer + /// + public bool IsPointer => !(this.ModelType.HasInterface() || IsRequired || ModelType.CanBeNull() || ModelType is EnumTypeGo); + /// /// Returns true if this property represents custom metadata. /// diff --git a/src/Templates/Marshalling.cshtml b/src/Templates/Marshalling.cshtml index 9c1ebe08a..83950dfd1 100644 --- a/src/Templates/Marshalling.cshtml +++ b/src/Templates/Marshalling.cshtml @@ -7,18 +7,6 @@ @using System.Linq @inherits AutoRest.Core.Template -package @Model.Namespace -@EmptyLine -@Header("// ") - -@EmptyLine - -import ( - "encoding/xml" - "reflect" - "time" - "unsafe" -) const ( rfc3339Format = "2006-01-02T15:04:05.0000000Z07:00" @@ -59,33 +47,13 @@ func (t *timeRFC3339) UnmarshalText(data []byte) (err error) { return } -@foreach (var ct in Model.RequiresMarshallers) +@foreach (var ct in Model.RequiresDateTimeCustomHandling) { var internalName = ct.Name.ToCamelCase(); - var rec = ct.Name.ToString().ToShortName(); - var local = $"{rec}2"; // internal type used for marshalling type @internalName struct { @ct.Fields(true) } - -// MarshalXML implements the xml.Marshaler interface for @ct.Name. -func (@rec @ct.Name) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - if reflect.TypeOf((*@ct.Name)(nil)).Elem().Size() != reflect.TypeOf((*@internalName)(nil)).Elem().Size() { - panic("size mismatch between @ct.Name and @internalName") - } - @local := (*@internalName)(unsafe.Pointer(&@rec)) - return e.EncodeElement(*@local, start) -} - -// UnmarshalXML implements the xml.Unmarshaler interface for @ct.Name. -func (@rec *@ct.Name) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - if reflect.TypeOf((*@ct.Name)(nil)).Elem().Size() != reflect.TypeOf((*@internalName)(nil)).Elem().Size() { - panic("size mismatch between @ct.Name and @internalName") - } - @local := (*@internalName)(unsafe.Pointer(@rec)) - return d.DecodeElement(@local, &start) -} } diff --git a/src/Templates/MarshallingJson.cshtml b/src/Templates/MarshallingJson.cshtml new file mode 100644 index 000000000..335deeaf0 --- /dev/null +++ b/src/Templates/MarshallingJson.cshtml @@ -0,0 +1,160 @@ +@using AutoRest.Core.Model +@using AutoRest.Core.Utilities +@using AutoRest.Go +@using AutoRest.Go.Model +@using AutoRest.Go.Templates +@using System.Collections.Generic +@using System.Linq +@inherits AutoRest.Core.Template +@EmptyLine + +@if (Model.HasInterface()) +{ + + func unmarshal@(Model.GetInterfaceName())(body []byte) (@(Model.GetInterfaceName()), error){ + var m map[string]interface{} + err := json.Unmarshal(body, &m) + if err != nil { + return nil, err + } + @EmptyLine + switch m["@(Model.RootType.PolymorphicDiscriminator)"] { + @foreach (var dt in Model.DerivedTypes) + { + + case string(@CodeNamerGo.Instance.GetEnumMemberName(((CompositeTypeGo) dt).DiscriminatorEnumValue)): + var @(dt.Name.FixedValue.ToShortName()) @(dt.Name) + err := json.Unmarshal(body, &@(dt.Name.FixedValue.ToShortName())) + return @(dt.Name.FixedValue.ToShortName()), err + + } + default: + var @(Model.Name.FixedValue.ToShortName()) @(Model.Name) + err := json.Unmarshal(body, &@(Model.Name.FixedValue.ToShortName())) + return @(Model.Name.FixedValue.ToShortName()), err + } + } + + func unmarshal@(Model.GetInterfaceName())Array(body []byte) ([]@(Model.GetInterfaceName()), error){ + var rawMessages []*json.RawMessage + err := json.Unmarshal(body, &rawMessages) + if err != nil { + return nil, err + } + @EmptyLine + @(Model.Name.FixedValue.ToShortName())Array := make([]@(Model.GetInterfaceName()), len(rawMessages)) + @EmptyLine + for index, rawMessage := range rawMessages { + @(Model.Name.FixedValue.ToShortName()), err := unmarshal@(Model.GetInterfaceName())(*rawMessage) + if err != nil { + return nil, err + } + @(Model.Name.FixedValue.ToShortName())Array[index] = @(Model.Name.FixedValue.ToShortName()) + } + return @(Model.Name.FixedValue.ToShortName())Array, nil + } + +} + +@if (Model.BaseIsPolymorphic || Model.IsPolymorphic) +{ + + @EmptyLine + // MarshalJSON is the custom marshaler for @(Model.Name). + func (@(Model.Name.FixedValue.ToShortName()) @(Model.Name))MarshalJSON() ([]byte, error){ + @(Model.Name.FixedValue.ToShortName()).@(Model.PolymorphicProperty) = @(Model.DiscriminatorEnumValue) + type Alias @(Model.Name) + return json.Marshal(&struct { + Alias + }{ + Alias: (Alias)(@(Model.Name.FixedValue.ToShortName())), + }) + } + +} + +@if (Model.HasPolymorphicFields || Model.HasFlattenedFields) +{ + + // UnmarshalJSON is the custom unmarshaler for @(Model.Name) struct. + func (@(Model.Name.FixedValue.ToShortName()) *@(Model.Name)) UnmarshalJSON(body []byte) error { + @if (Model.IsWrapperType) + { + if (Model.BaseType is SequenceTypeGo sequenceType) + { + @:@(sequenceType.ElementType.Name.FixedValue.ToShortName()), err := unmarshal@(sequenceType.ElementType.GetInterfaceName())Array(body) + } + else + { + @:@(Model.BaseType.Name.FixedValue.ToShortName()), err := unmarshal@(Model.BaseType.GetInterfaceName())(body) + } + + if err != nil { + return err + } + + if (Model.BaseType is SequenceType type) + { + @:@(Model.Name.FixedValue.ToShortName()).Value = &@type.ElementType.Name.FixedValue.ToShortName() + } + else + { + @:@(Model.Name.FixedValue.ToShortName()).Value = @(Model.BaseType.Name.FixedValue.ToShortName()) + } +} +else +{ + + var m map[string]*json.RawMessage + err := json.Unmarshal(body, &m) + if err != nil { + return err + } + + var v *json.RawMessage + @foreach (var p in Model.AllProperties) + { + + @EmptyLine + v = m["@(p.SerializedName)"] + if v != nil { + @if (p.ModelType.HasInterface()) + { + @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(p.ModelType.GetInterfaceName())(*m["@(p.SerializedName)"]) + } + else if (p.ModelType is SequenceTypeGo sequenceType && sequenceType.ElementType.HasInterface()) + { + @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(sequenceType.ElementType.GetInterfaceName())Array(*m["@(p.SerializedName)"]) + } + else if (p.ModelType is CompositeType && ((CompositeTypeGo)p.ModelType).IsPolymorphic) + { + @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(p.ModelType.Name)(*m["@(p.SerializedName)"]) + } + else + { + + var @(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) @(p.ModelType.Name) + err = json.Unmarshal(*m["@(p.SerializedName)"], &@(CodeNamerGo.Instance.GetVariableName(p.SerializedName))) + +} + if err != nil { + return err + } + @if (p.ModelType.HasInterface() || p.ModelType is EnumType) + { + @:@(Model.Name.FixedValue.ToShortName()).@(p.Name) = @(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) + } + else + { + @:@(Model.Name.FixedValue.ToShortName()).@(p.Name) = &@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) + } + } + + } + + } + @EmptyLine + return nil + } + + } \ No newline at end of file diff --git a/src/Templates/MarshallingXml.cshtml b/src/Templates/MarshallingXml.cshtml new file mode 100644 index 000000000..8cec1b788 --- /dev/null +++ b/src/Templates/MarshallingXml.cshtml @@ -0,0 +1,137 @@ +@using AutoRest.Core.Model +@using AutoRest.Core.Utilities +@using AutoRest.Go +@using AutoRest.Go.Model +@using AutoRest.Go.Templates +@using System.Collections.Generic +@using System.Linq +@inherits AutoRest.Core.Template +@EmptyLine + +@if (Model.HasInterface()) +{ + + func unmarshal@(Model.GetInterfaceName())(d *xml.Decoder, start xml.StartElement) (@(Model.GetInterfaceName()), error){ + switch start.Name.Local { + @foreach (var dt in Model.DerivedTypes) + { + + case string(@CodeNamerGo.Instance.GetEnumMemberName(((CompositeTypeGo)dt).DiscriminatorEnumValue)): + var @(dt.Name.FixedValue.ToShortName()) @(dt.Name) + err := d.DecodeElement(&@(dt.Name.FixedValue.ToShortName()), &start) + return @(dt.Name.FixedValue.ToShortName()), err + +} + default: + var @(Model.Name.FixedValue.ToShortName()) @(Model.Name) + err := d.DecodeElement(&@(Model.Name.FixedValue.ToShortName()), &start) + return @(Model.Name.FixedValue.ToShortName()), err + } + + } + + func unmarshal@(Model.GetInterfaceName())Array(d *xml.Decoder, start xml.StartElement) ([]@(Model.GetInterfaceName()), error){ + @(Model.Name.FixedValue.ToShortName())Array := []@(Model.GetInterfaceName()){} + + for t, err := d.Token(); err == nil; t, err = d.Token() { + tt, ok := t.(xml.StartElement) + if !ok { + continue + } + @(Model.Name.FixedValue.ToShortName()), err := unmarshal@(Model.GetInterfaceName())(d, tt) + if err == nil { + @(Model.Name.FixedValue.ToShortName())Array = append(@(Model.Name.FixedValue.ToShortName())Array, @(Model.Name.FixedValue.ToShortName())) + } + } + return @(Model.Name.FixedValue.ToShortName())Array, nil + } + + } + +@if (Model.BaseIsPolymorphic || Model.IsPolymorphic) +{ + + @EmptyLine + // MarshalXML is the custom marshaler for @(Model.Name). + func (@(Model.Name.FixedValue.ToShortName()) @(Model.Name))MarshalXML(e *xml.Encoder, start xml.StartElement) error { + @(Model.Name.FixedValue.ToShortName()).@(Model.PolymorphicProperty) = @(Model.DiscriminatorEnumValue) + type Alias @(Model.Name) + return e.EncodeElement(struct { + Alias + }{ + Alias: (Alias)(@(Model.Name.FixedValue.ToShortName())), + }, start) + } + +} + +@if (Model.HasPolymorphicFields || Model.HasFlattenedFields) +{ + var attrProperties = Model.AllProperties.Where(p => p.XmlIsAttribute); + var nonAttrProperties = Model.AllProperties.Where(p => !p.XmlIsAttribute); + + + // UnmarshalXML is the custom unmarshaler for @(Model.Name) struct. + func (@(Model.Name.FixedValue.ToShortName()) *@(Model.Name)) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + + for _, a := range start.Attr { + switch a.Name.Local { + @foreach (var p in attrProperties) + { + + case "@p.Name": + @if (p.IsPointer) + { + @:@(Model.Name.FixedValue.ToShortName()).@p.Name = &a.Value + } + else + { + @:@(Model.Name.FixedValue.ToShortName()).@p.Name = a.Value + } + + } + } + } + + for t, err := d.Token(); err == nil; t, err = d.Token() { + tt, ok := t.(xml.StartElement) + if !ok { + continue + } + switch tt.Name.Local { + @foreach (var p in nonAttrProperties) + { + @:case "@p.Name": + + @if (p.ModelType.HasInterface()) + { + @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(p.ModelType.GetInterfaceName())(d, tt) + @:if err != nil { + @: return err + @:} + @:@(Model.Name.FixedValue.ToShortName()).@(p.Name) = @(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) + } + else if (p.ModelType is SequenceTypeGo sequenceType && sequenceType.ElementType.HasInterface()) + { + @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(sequenceType.ElementType.GetInterfaceName())Array(d, tt) + @:if err != nil { + @: return err + @:} + @:@(Model.Name.FixedValue.ToShortName()).@(p.Name) = @(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) + } + else + { + @:err := d.DecodeElement(&@(Model.Name.FixedValue.ToShortName()).@p.Name, &tt) + @:if err != nil { + @: return err + @:} + } + + + } + } + } + return nil + } + + } diff --git a/src/Templates/ModelTemplate.cshtml b/src/Templates/ModelTemplate.cshtml index 7cb9d292e..e7986c804 100644 --- a/src/Templates/ModelTemplate.cshtml +++ b/src/Templates/ModelTemplate.cshtml @@ -35,50 +35,6 @@ @(Model.AddHTTPResponse()) @(Model.Fields(forMarshaller: false)) } - - @EmptyLine - func unmarshal@(Model.GetInterfaceName())(body []byte) (@(Model.GetInterfaceName()), error){ - var m map[string]interface{} - err := json.Unmarshal(body, &m) - if err != nil { - return nil, err - } - @EmptyLine - switch m["@(Model.RootType.PolymorphicDiscriminator)"] { - @foreach (var dt in Model.DerivedTypes) - { - - case string(@(CodeNamerGo.Instance.GetEnumMemberName((dt as CompositeTypeGo).DiscriminatorEnumValue))): - var @(dt.Name.FixedValue.ToShortName()) @(dt.Name) - err := json.Unmarshal(body, &@(dt.Name.FixedValue.ToShortName())) - return @(dt.Name.FixedValue.ToShortName()), err - - } - default: - var @(Model.Name.FixedValue.ToShortName()) @(Model.Name) - err := json.Unmarshal(body, &@(Model.Name.FixedValue.ToShortName())) - return @(Model.Name.FixedValue.ToShortName()), err - } - } - - func unmarshal@(Model.GetInterfaceName())Array(body []byte) ([]@(Model.GetInterfaceName()), error){ - var rawMessages []*json.RawMessage - err := json.Unmarshal(body, &rawMessages) - if err != nil { - return nil, err - } - @EmptyLine - @(Model.Name.FixedValue.ToShortName())Array := make([]@(Model.GetInterfaceName()), len(rawMessages)) - @EmptyLine - for index, rawMessage := range rawMessages { - @(Model.Name.FixedValue.ToShortName()), err := unmarshal@(Model.GetInterfaceName())(*rawMessage) - if err != nil { - return nil, err - } - @(Model.Name.FixedValue.ToShortName())Array[index] = @(Model.Name.FixedValue.ToShortName()) - } - return @(Model.Name.FixedValue.ToShortName())Array, nil - } } else @@ -96,142 +52,53 @@ else } -@if (Model.BaseIsPolymorphic && Model.IsPolymorphic) +@if (Model.BaseIsPolymorphic || Model.IsPolymorphic) { - - @EmptyLine - // MarshalJSON is the custom marshaler for @(Model.Name). - func (@(Model.Name.FixedValue.ToShortName()) @(Model.Name))MarshalJSON() ([]byte, error){ - @(Model.Name.FixedValue.ToShortName()).@(Model.PolymorphicProperty) = @(Model.DiscriminatorEnumValue) - type Alias @(Model.Name) - return json.Marshal(&struct { - Alias - }{ - Alias: (Alias)(@(Model.Name.FixedValue.ToShortName())), - }) - } - - @foreach (var st in Model.SiblingTypes) - { - - @EmptyLine - // As@(st.Name) is the @(Model.RootType.GetInterfaceName()) implementation for @(Model.Name). - func (@(Model.Name.FixedValue.ToShortName()) @(Model.Name)) As@(st.Name)() (*@(st.Name), bool) { - @if (st.Equals(Model)) - { - @:return &@(Model.Name.FixedValue.ToShortName()), true - } - else - { - @:return nil, false - } - } - @if (st.HasInterface()) - { - @EmptyLine - @:// As@(st.GetInterfaceName()) is the @(Model.RootType.GetInterfaceName()) implementation for @(Model.Name). - @:func(@(Model.Name.FixedValue.ToShortName()) @(Model.Name)) As@(st.GetInterfaceName())()(@(st.GetInterfaceName()), bool) { - if (st.Equals(Model) || Model.DerivesFrom(st)) - { - @:return &@(Model.Name.FixedValue.ToShortName()), true - } - else - { - @:return nil, false - } - @:} - @EmptyLine + foreach (var st in Model.SiblingTypes) + { + + @EmptyLine + // As@(st.Name) is the @(Model.RootType.GetInterfaceName()) implementation for @(Model.Name). + func (@(Model.Name.FixedValue.ToShortName()) @(Model.Name)) As@(st.Name)() (*@(st.Name), bool) { + @if (st.Equals(Model)) + { + @:return &@(Model.Name.FixedValue.ToShortName()), true } - -} - + else + { + @:return nil, false + } + } + @if (st.HasInterface()) + { + @EmptyLine + @:// As@(st.GetInterfaceName()) is the @(Model.RootType.GetInterfaceName()) implementation for @(Model.Name). + @:func(@(Model.Name.FixedValue.ToShortName()) @(Model.Name)) As@(st.GetInterfaceName())()(@(st.GetInterfaceName()), bool) { + if (st.Equals(Model) || Model.DerivesFrom(st)) + { + @:return &@(Model.Name.FixedValue.ToShortName()), true + } + else + { + @:return nil, false + } + @:} + @EmptyLine + } + + } } -@if (Model.HasPolymorphicFields && Model.HasFlattenedFields) -{ - - // UnmarshalJSON is the custom unmarshaler for @(Model.Name) struct. - func (@(Model.Name.FixedValue.ToShortName()) *@(Model.Name)) UnmarshalJSON(body []byte) error { - @if (Model.IsWrapperType) - { - if (Model.BaseType is SequenceTypeGo sequenceType) - { - @:@(sequenceType.ElementType.Name.FixedValue.ToShortName()), err := unmarshal@(sequenceType.ElementType.GetInterfaceName())Array(body) - } - else - { - @:@(Model.BaseType.Name.FixedValue.ToShortName()), err := unmarshal@(Model.BaseType.GetInterfaceName())(body) - } - - if err != nil { - return err - } - - if (Model.BaseType is SequenceType type) - { - @:@(Model.Name.FixedValue.ToShortName()).Value = &@type.ElementType.Name.FixedValue.ToShortName() - } - else - { - @:@(Model.Name.FixedValue.ToShortName()).Value = @(Model.BaseType.Name.FixedValue.ToShortName()) - } - } - else - { - - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - var v *json.RawMessage - @foreach (var p in Model.AllProperties) - { - - @EmptyLine - v = m["@(p.SerializedName)"] - if v != nil { - @if (p.ModelType.HasInterface()) - { - @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(p.ModelType.GetInterfaceName())(*m["@(p.SerializedName)"]) - } - else if (p.ModelType is SequenceTypeGo sequenceType && sequenceType.ElementType.HasInterface()) - { - @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(sequenceType.ElementType.GetInterfaceName())Array(*m["@(p.SerializedName)"]) - } - else if (p.ModelType is CompositeType && ((CompositeTypeGo) p.ModelType).IsPolymorphic) - { - @:@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)), err := unmarshal@(p.ModelType.Name)(*m["@(p.SerializedName)"]) - } - else - { - - var @(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) @(p.ModelType.Name) - err = json.Unmarshal(*m["@(p.SerializedName)"], &@(CodeNamerGo.Instance.GetVariableName(p.SerializedName))) - - } - if err != nil { - return err - } - @if (p.ModelType.HasInterface() || p.ModelType is EnumType) - { - @:@(Model.Name.FixedValue.ToShortName()).@(p.Name) = @(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) - } - else - { - @:@(Model.Name.FixedValue.ToShortName()).@(p.Name) = &@(CodeNamerGo.Instance.GetVariableName(p.SerializedName)) - } - } - - } - - } - @EmptyLine - return nil - } - +@if (Model.CodeModel.ShouldGenerateXmlSerialization) +{ + @(Include(new MarshallingXml(), Model)) } +else +{ + @(Include(new MarshallingJson(), Model)) +} + @EmptyLine @if (Model.IsResponseType) { @@ -373,3 +240,29 @@ else } } + +@if (Model.IsDateTimeCustomHandlingRequired) +{ + var internalName = Model.Name.ToCamelCase(); + var rec = Model.Name.ToString().ToShortName(); + var local = $"{rec}2"; + +// MarshalXML implements the xml.Marshaler interface for @Model.Name. + func (@rec @Model.Name) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if reflect.TypeOf((*@Model.Name)(nil)).Elem().Size() != reflect.TypeOf((*@internalName)(nil)).Elem().Size() { + panic("size mismatch between @Model.Name and @internalName") + } + @local := (*@internalName)(unsafe.Pointer(&@rec)) + return e.EncodeElement(*@local, start) + } + +// UnmarshalXML implements the xml.Unmarshaler interface for @Model.Name. + func (@rec *@Model.Name) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + if reflect.TypeOf((*@Model.Name)(nil)).Elem().Size() != reflect.TypeOf((*@internalName)(nil)).Elem().Size() { + panic("size mismatch between @Model.Name and @internalName") + } + @local := (*@internalName)(unsafe.Pointer(@rec)) + return d.DecodeElement(@local, &start) + } + +} diff --git a/src/Templates/ModelsTemplate.cshtml b/src/Templates/ModelsTemplate.cshtml index b94a6768c..cc3c5cd36 100644 --- a/src/Templates/ModelsTemplate.cshtml +++ b/src/Templates/ModelsTemplate.cshtml @@ -1,128 +1,130 @@ -@using AutoRest.Core.Model -@using AutoRest.Core.Utilities -@using AutoRest.Go -@using AutoRest.Go.Model -@using AutoRest.Go.Templates -@using System -@using System.Collections.Generic -@using System.Linq - -@inherits AutoRest.Core.Template - -package @Model.Namespace -@EmptyLine -@Header("// ") - -@EmptyLine - -@if (!Model.ModelImports.IsNullOrEmpty()) -{ -@:import ( -foreach (var import in Model.ModelImports) -{ - @:@(import) -} -@:) -@EmptyLine -} - -@if (Model.UsesETags) -{ - -// @CodeNamerGo.Instance.ETagTypeName is an entity tag. -type @CodeNamerGo.Instance.ETagTypeName string - -const ( - // ETagNone represents an empty entity tag. - ETagNone @CodeNamerGo.Instance.ETagTypeName = "" -@EmptyLine - // ETagAny matches any entity tag. - ETagAny @CodeNamerGo.Instance.ETagTypeName = "*" -) - -} -@if (Model.UsesMetadataType) -{ - -// Metadata contains metadata key/value pairs. -type Metadata map[string]string -@EmptyLine -const mdPrefix = "x-ms-meta-" -@EmptyLine -const mdPrefixLen = len(mdPrefix) -@EmptyLine -// UnmarshalXML implements the xml.Unmarshaler interface for Metadata. -func (md *Metadata) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - tokName := "" - - for t, err := d.Token(); err == nil; t, err = d.Token() { - switch tt := t.(type) { - case xml.StartElement: - tokName = strings.ToLower(tt.Name.Local) - break - case xml.CharData: - if *md == nil { - *md = Metadata{} - } - (*md)[tokName] = string(tt) - - break - - } - } - return nil -} - -} -@if (Model.PagedTypes.Any()) -{ - -// Marker represents an opaque value used in paged responses. -type Marker struct { - val *string -} - -// NotDone returns true if the list enumeration should be started or is not yet complete. Specifically, NotDone returns true -// for a just-initialized (zero value) Marker indicating that you should make an initial request to get a result portion from -// the service. NotDone also returns true whenever the service returns an interim result portion. NotDone returns false only -// after the service has returned the final result portion. -func (m Marker) NotDone() bool { - return m.val == nil || *m.val != "" -} - -// UnmarshalXML implements the xml.Unmarshaler interface for Marker. -func (m *Marker) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - var out string - err := d.DecodeElement(&out, &start) - m.val = &out - return err -} - -} -// concatenates a slice of const values with the specified separator between each item -func joinConst(s interface{}, sep string) string { - v := reflect.ValueOf(s) - if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { - panic("s wasn't a slice or array") - } - ss := make([]string, 0, v.Len()) - for i := 0; i < v.Len(); i++ { - ss = append(ss, v.Index(i).String()) - } - return strings.Join(ss, sep) -} -@foreach (var e in Model.Enums) -{ - -@(Include(new EnumTemplate(), e)) -@EmptyLine - -} - -@foreach (var mt in Model.Models) -{ - -@(Include(new ModelTemplate(), mt)) -@EmptyLine - -} +@using AutoRest.Core.Model +@using AutoRest.Core.Utilities +@using AutoRest.Go +@using AutoRest.Go.Model +@using AutoRest.Go.Templates +@using System +@using System.Collections.Generic +@using System.Linq +@inherits AutoRest.Core.Template +package @Model.Namespace +@EmptyLine +@Header("// ") +@EmptyLine +@if (!Model.ModelImports.IsNullOrEmpty()) +{ + @:import ( +foreach (var import in Model.ModelImports) + { + @:@(import) +} + @:) + @EmptyLine +} +@if (Model.UsesETags) +{ + + // @CodeNamerGo.Instance.ETagTypeName is an entity tag. + type @CodeNamerGo.Instance.ETagTypeName string + + const ( + // ETagNone represents an empty entity tag. + ETagNone @CodeNamerGo.Instance.ETagTypeName = "" + @EmptyLine + // ETagAny matches any entity tag. + ETagAny @CodeNamerGo.Instance.ETagTypeName = "*" + ) + +} +@if (Model.UsesMetadataType && Model.ShouldGenerateXmlSerialization) +{ + + // Metadata contains metadata key/value pairs. + type Metadata map[string]string + @EmptyLine + const mdPrefix = "x-ms-meta-" + @EmptyLine + const mdPrefixLen = len(mdPrefix) + @EmptyLine + // UnmarshalXML implements the xml.Unmarshaler interface for Metadata. + func (md *Metadata) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + tokName := "" + + for t, err := d.Token(); err == nil; t, err = d.Token() { + switch tt := t.(type) { + case xml.StartElement: + tokName = strings.ToLower(tt.Name.Local) + break + case xml.CharData: + if *md == nil { + *md = Metadata{} + } + (*md)[tokName] = string(tt) + + break + + } + } + return nil + } + +} + +@if (Model.PagedTypes.Any() && Model.ShouldGenerateXmlSerialization) +{ + +// Marker represents an opaque value used in paged responses. +type Marker struct { + val *string +} + +// NotDone returns true if the list enumeration should be started or is not yet complete. Specifically, NotDone returns true +// for a just-initialized (zero value) Marker indicating that you should make an initial request to get a result portion from +// the service. NotDone also returns true whenever the service returns an interim result portion. NotDone returns false only +// after the service has returned the final result portion. +func (m Marker) NotDone() bool { + return m.val == nil || *m.val != "" +} + +// UnmarshalXML implements the xml.Unmarshaler interface for Marker. +func (m *Marker) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + var out string + err := d.DecodeElement(&out, &start) + m.val = &out + return err +} + +} + +// concatenates a slice of const values with the specified separator between each item +func joinConst(s interface{}, sep string) string { + v := reflect.ValueOf(s) + if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { + panic("s wasn't a slice or array") + } + ss := make([]string, 0, v.Len()) + for i := 0; i < v.Len(); i++ { + ss = append(ss, v.Index(i).String()) + } + return strings.Join(ss, sep) +} +@foreach (var e in Model.Enums) +{ + + @(Include(new EnumTemplate(), e)) + @EmptyLine + +} +@foreach (var mt in Model.Models) +{ + + @(Include(new ModelTemplate(), mt)) + @EmptyLine + +} +@if (Model.RequiresDateTimeCustomHandling.Any()) +{ + + @(Include(new Marshalling(), Model)) + +} diff --git a/src/autorest.go.csproj b/src/autorest.go.csproj index d59284713..c8990689f 100644 --- a/src/autorest.go.csproj +++ b/src/autorest.go.csproj @@ -1,4 +1,4 @@ - + # 2>nul || type %~df0|C:\Windows\system32\find.exe /v "setlocal"|C:\Windows\system32\find.exe /v "errorlevel"|powershell.exe -noninteractive -& exit %errorlevel% || # @@ -74,5 +74,11 @@ All + + + + + + From 415e4e191d40ff6869ba71eb4d2a4e69f5681225 Mon Sep 17 00:00:00 2001 From: Vlad Barosan Date: Thu, 22 Mar 2018 15:56:26 -0700 Subject: [PATCH 06/10] Fix and regenerate tests --- .../stringgrouptest/body-string_test.go | 12 ++++---- .../src/tests/generated/body-string/models.go | 29 +++++++++++-------- .../generated/httpinfrastructure/models.go | 12 ++++---- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/test/src/tests/acceptancetests/stringgrouptest/body-string_test.go b/test/src/tests/acceptancetests/stringgrouptest/body-string_test.go index 9ec0799e5..f7ad55724 100644 --- a/test/src/tests/acceptancetests/stringgrouptest/body-string_test.go +++ b/test/src/tests/acceptancetests/stringgrouptest/body-string_test.go @@ -43,14 +43,14 @@ const ( func (s *StringSuite) TestGetEmptyString(c *chk.C) { str, err := stringClient.GetEmpty(context.Background()) c.Assert(err, chk.IsNil) - c.Assert(*str.Value, chk.HasLen, 0) - c.Assert(*str.Value, chk.Equals, emptyString) + c.Assert(str.Value, chk.HasLen, 0) + c.Assert(str.Value, chk.Equals, emptyString) } func (s *StringSuite) TestGetMbcs(c *chk.C) { str, err := stringClient.GetMbcs(context.Background()) c.Assert(err, chk.IsNil) - c.Assert(*str.Value, chk.Equals, multibyteBufferBody) + c.Assert(str.Value, chk.Equals, multibyteBufferBody) } func (s *StringSuite) TestGetNotProvided(c *chk.C) { @@ -62,13 +62,13 @@ func (s *StringSuite) TestGetNotProvided(c *chk.C) { func (s *StringSuite) TestGetNullString(c *chk.C) { str, err := stringClient.GetNull(context.Background()) c.Assert(err, chk.IsNil) - c.Assert(str.Value, chk.IsNil) + c.Assert(str.Value, chk.Equals, "") } func (s *StringSuite) TestGetWhitespace(c *chk.C) { str, err := stringClient.GetWhitespace(context.Background()) c.Assert(err, chk.IsNil) - c.Assert(*str.Value, chk.Equals, whitespaceText) + c.Assert(str.Value, chk.Equals, whitespaceText) } func (s *StringSuite) TestPutEmptyString(c *chk.C) { @@ -124,7 +124,7 @@ func (s *StringSuite) TestPutWhitespace(c *chk.C) { func (s *StringSuite) TestGetNotExpandable(c *chk.C) { str, err := enumClient.GetNotExpandable(context.Background()) c.Assert(err, chk.IsNil) - c.Assert(str.Value, chk.Equals, ColorsRedcolor) + c.Assert(str.Value, chk.Equals, Redcolor) } func (s *StringSuite) TestPutNotExpandable(c *chk.C) { diff --git a/test/src/tests/generated/body-string/models.go b/test/src/tests/generated/body-string/models.go index 02feefbab..d0b89e5c0 100644 --- a/test/src/tests/generated/body-string/models.go +++ b/test/src/tests/generated/body-string/models.go @@ -29,16 +29,21 @@ func joinConst(s interface{}, sep string) string { type ColorsType string const ( - // ColorsBlueColor ... - ColorsBlueColor ColorsType = "blue_color" - // ColorsGreenColor ... - ColorsGreenColor ColorsType = "green-color" - // ColorsNone represents an empty ColorsType. - ColorsNone ColorsType = "" - // ColorsRedcolor ... - ColorsRedcolor ColorsType = "red color" + // BlueColor ... + BlueColor ColorsType = "blue_color" + // GreenColor ... + GreenColor ColorsType = "green-color" + // None ColorsNone represents an empty ColorsType. + None ColorsType = "" + // Redcolor ... + Redcolor ColorsType = "red color" ) +// PossibleColorsTypeValues returns an array of possible values for the ColorsType const type. +func PossibleColorsTypeValues() []ColorsType { + return []ColorsType{BlueColor, GreenColor, None, Redcolor} +} + // Error ... type Error struct { Status *int32 `json:"status,omitempty"` @@ -91,7 +96,7 @@ func (gb6er GetBase64URLEncodedResponse) Status() string { type GetEmptyResponse struct { rawResponse *http.Response // Value - Possible values include: '' - Value *string `json:"value,omitempty"` + Value string `json:"value,omitempty"` } // Response returns the raw HTTP response object. @@ -113,7 +118,7 @@ func (ger GetEmptyResponse) Status() string { type GetMbcsResponse struct { rawResponse *http.Response // Value - Possible values include: '啊齄丂狛狜隣郎隣兀﨩ˊ〞〡¦℡㈱‐ー﹡﹢﹫、〓ⅰⅹ⒈€㈠㈩ⅠⅫ! ̄ぁんァヶΑ︴АЯаяāɡㄅㄩ─╋︵﹄︻︱︳︴ⅰⅹɑɡ〇〾⿻⺁䜣€' - Value *string `json:"value,omitempty"` + Value string `json:"value,omitempty"` } // Response returns the raw HTTP response object. @@ -199,7 +204,7 @@ func (gnb6er GetNullBase64URLEncodedResponse) Status() string { type GetNullResponse struct { rawResponse *http.Response // Value - Possible values include: '' - Value *string `json:"value,omitempty"` + Value string `json:"value,omitempty"` } // Response returns the raw HTTP response object. @@ -243,7 +248,7 @@ func (grr GetReferencedResponse) Status() string { type GetWhitespaceResponse struct { rawResponse *http.Response // Value - Possible values include: ' Now is the time for all good men to come to the aid of their country ' - Value *string `json:"value,omitempty"` + Value string `json:"value,omitempty"` } // Response returns the raw HTTP response object. diff --git a/test/src/tests/generated/httpinfrastructure/models.go b/test/src/tests/generated/httpinfrastructure/models.go index 41649eaa9..a8652ec00 100644 --- a/test/src/tests/generated/httpinfrastructure/models.go +++ b/test/src/tests/generated/httpinfrastructure/models.go @@ -70,18 +70,18 @@ type Error struct { } // Response returns the raw HTTP response object. -func (e Error) Response() *http.Response { - return e.rawResponse +func (eVar Error) Response() *http.Response { + return eVar.rawResponse } // StatusCode returns the HTTP status code of the response, e.g. 200. -func (e Error) StatusCode() int { - return e.rawResponse.StatusCode +func (eVar Error) StatusCode() int { + return eVar.rawResponse.StatusCode } // HTTPStatus returns the HTTP status message of the response, e.g. "200 OK". -func (e Error) HTTPStatus() string { - return e.rawResponse.Status +func (eVar Error) HTTPStatus() string { + return eVar.rawResponse.Status } // Get200ModelA201ModelC404ModelDDefaultError200ValidResponse ... From 236966b0a48b23fd0ad055463554ac34025772aa Mon Sep 17 00:00:00 2001 From: Vlad Barosan Date: Thu, 29 Mar 2018 10:13:48 -0700 Subject: [PATCH 07/10] Populate discriminator field on unmarshalling --- src/Templates/EnumTemplate.cshtml | 3 -- src/Templates/MarshallingXml.cshtml | 76 ++++++++++++++++++++++++----- src/Templates/ModelTemplate.cshtml | 43 ---------------- 3 files changed, 64 insertions(+), 58 deletions(-) diff --git a/src/Templates/EnumTemplate.cshtml b/src/Templates/EnumTemplate.cshtml index 88b95896b..84a34aa6d 100644 --- a/src/Templates/EnumTemplate.cshtml +++ b/src/Templates/EnumTemplate.cshtml @@ -4,9 +4,6 @@ @using System.Linq @inherits AutoRest.Core.Template -@{ - var values = Model.Values.Cast().OrderBy(v => v.Name); -} @WrapComment("// ", Model.Documentation) type @Model.Name string diff --git a/src/Templates/MarshallingXml.cshtml b/src/Templates/MarshallingXml.cshtml index 8cec1b788..f1194f994 100644 --- a/src/Templates/MarshallingXml.cshtml +++ b/src/Templates/MarshallingXml.cshtml @@ -19,12 +19,18 @@ case string(@CodeNamerGo.Instance.GetEnumMemberName(((CompositeTypeGo)dt).DiscriminatorEnumValue)): var @(dt.Name.FixedValue.ToShortName()) @(dt.Name) err := d.DecodeElement(&@(dt.Name.FixedValue.ToShortName()), &start) + if err == nil { + @(dt.Name.FixedValue.ToShortName()).@(Model.DiscriminatorEnum.Name.Value) = @CodeNamerGo.Instance.GetEnumMemberName(((CompositeTypeGo)dt).DiscriminatorEnumValue) + } return @(dt.Name.FixedValue.ToShortName()), err } default: var @(Model.Name.FixedValue.ToShortName()) @(Model.Name) err := d.DecodeElement(&@(Model.Name.FixedValue.ToShortName()), &start) + if err == nil { + @(Model.Name.FixedValue.ToShortName()).@(Model.DiscriminatorEnum.Name.Value) = @CodeNamerGo.Instance.GetEnumMemberName(((CompositeTypeGo)Model).DiscriminatorEnumValue) + } return @(Model.Name.FixedValue.ToShortName()), err } @@ -79,17 +85,17 @@ @foreach (var p in attrProperties) { - case "@p.Name": - @if (p.IsPointer) - { - @:@(Model.Name.FixedValue.ToShortName()).@p.Name = &a.Value - } - else - { - @:@(Model.Name.FixedValue.ToShortName()).@p.Name = a.Value - } - - } + case "@p.Name": + @if (p.IsPointer) + { + @:@(Model.Name.FixedValue.ToShortName()).@p.Name = &a.Value + } + else + { + @:@(Model.Name.FixedValue.ToShortName()).@p.Name = a.Value + } + +} } } @@ -131,7 +137,53 @@ } } } - return nil + return nil } } + +@if (Model.ResponseIncludesMetadata) +{ + var shortName = Model.Name.ToString().ToShortName(); + + + // NewMetadata returns user-defined key/value pairs. + func(@shortName @Model.Name) NewMetadata() Metadata { + md := Metadata{} + for k, v := range @(shortName).rawResponse.Header { + if len(k) > mdPrefixLen { + if prefix := k[0:mdPrefixLen]; strings.EqualFold(prefix, mdPrefix) { + md[strings.ToLower(k[mdPrefixLen:])] = v[0] + } + } + } + return md + } + +} + +@if (Model.IsDateTimeCustomHandlingRequired) +{ + var internalName = Model.Name.ToCamelCase(); + var rec = Model.Name.ToString().ToShortName(); + var local = $"{rec}2"; + + // MarshalXML implements the xml.Marshaler interface for @Model.Name. + func (@rec @Model.Name) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if reflect.TypeOf((*@Model.Name)(nil)).Elem().Size() != reflect.TypeOf((*@internalName)(nil)).Elem().Size() { + panic("size mismatch between @Model.Name and @internalName") + } + @local := (*@internalName)(unsafe.Pointer(&@rec)) + return e.EncodeElement(*@local, start) + } + + // UnmarshalXML implements the xml.Unmarshaler interface for @Model.Name. + func (@rec *@Model.Name) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + if reflect.TypeOf((*@Model.Name)(nil)).Elem().Size() != reflect.TypeOf((*@internalName)(nil)).Elem().Size() { + panic("size mismatch between @Model.Name and @internalName") + } + @local := (*@internalName)(unsafe.Pointer(@rec)) + return d.DecodeElement(@local, &start) + } + +} \ No newline at end of file diff --git a/src/Templates/ModelTemplate.cshtml b/src/Templates/ModelTemplate.cshtml index e7986c804..21aaa08ec 100644 --- a/src/Templates/ModelTemplate.cshtml +++ b/src/Templates/ModelTemplate.cshtml @@ -223,46 +223,3 @@ else } @:} } -@if (Model.ResponseIncludesMetadata) -{ - - // NewMetadata returns user-defined key/value pairs. - func(@shortName @Model.Name) NewMetadata() Metadata { - md := Metadata{} - for k, v := range @(shortName).rawResponse.Header { - if len(k) > mdPrefixLen { - if prefix := k[0:mdPrefixLen]; strings.EqualFold(prefix, mdPrefix) { - md[strings.ToLower(k[mdPrefixLen:])] = v[0] - } - } - } - return md - } - -} - -@if (Model.IsDateTimeCustomHandlingRequired) -{ - var internalName = Model.Name.ToCamelCase(); - var rec = Model.Name.ToString().ToShortName(); - var local = $"{rec}2"; - -// MarshalXML implements the xml.Marshaler interface for @Model.Name. - func (@rec @Model.Name) MarshalXML(e *xml.Encoder, start xml.StartElement) error { - if reflect.TypeOf((*@Model.Name)(nil)).Elem().Size() != reflect.TypeOf((*@internalName)(nil)).Elem().Size() { - panic("size mismatch between @Model.Name and @internalName") - } - @local := (*@internalName)(unsafe.Pointer(&@rec)) - return e.EncodeElement(*@local, start) - } - -// UnmarshalXML implements the xml.Unmarshaler interface for @Model.Name. - func (@rec *@Model.Name) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - if reflect.TypeOf((*@Model.Name)(nil)).Elem().Size() != reflect.TypeOf((*@internalName)(nil)).Elem().Size() { - panic("size mismatch between @Model.Name and @internalName") - } - @local := (*@internalName)(unsafe.Pointer(@rec)) - return d.DecodeElement(@local, &start) - } - -} From 77181762c86531e649cfc4293f32b1ba8cbb8613 Mon Sep 17 00:00:00 2001 From: Vlad Barosan Date: Fri, 30 Mar 2018 11:42:51 -0700 Subject: [PATCH 08/10] Fix enum names and unmarshalling bug --- src/Model/EnumValueGo.cs | 2 +- src/Model/ParameterGo.cs | 14 ++++++--- src/Templates/MarshallingXml.cshtml | 15 ++++++---- src/TransformerGo.cs | 30 +++++++++++++------ .../src/tests/generated/body-string/models.go | 24 +++++++-------- 5 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/Model/EnumValueGo.cs b/src/Model/EnumValueGo.cs index 0e2ed2da4..76a7a22ce 100644 --- a/src/Model/EnumValueGo.cs +++ b/src/Model/EnumValueGo.cs @@ -18,7 +18,7 @@ public static string FormatName(EnumTypeGo parent, EnumValueGo value) // TODO: ideally the core would set the Parent field to that of the EnumTypeGo // to which this belongs, for now this is how we work around that. var parentName = parent.Name.ToString(); - return $"{parentName.Substring(0, parentName.Length - 4)}{value.Name}"; + return $"{parentName.Substring(0, parentName.Length - 4)}{value.MemberName}"; } } } diff --git a/src/Model/ParameterGo.cs b/src/Model/ParameterGo.cs index e71c06226..5f3693956 100644 --- a/src/Model/ParameterGo.cs +++ b/src/Model/ParameterGo.cs @@ -123,7 +123,7 @@ public string DefaultValueString } /// - /// Get Name for parameter for Go map. + /// Get Name for parameter for Go map. /// If parameter is client parameter, then return client. /// /// @@ -275,7 +275,13 @@ public string GetOptionalComparand() return "nil"; } - return $"{et.Name}None"; + var typeName = et.Name.ToString(); + if (typeName.EndsWith("Type")) + { + typeName = typeName.Substring(0, typeName.Length - 4); + } + + return $"{typeName}None"; } /// @@ -300,7 +306,7 @@ public static class ParameterGoExtensions /// public static string Format(this ParameterGo parameter) { - return parameter.IsPassedByValue() ? "{0}" : "*{0}"; + return parameter.IsPassedByValue() ? "{0}" : "*{0}"; } /// @@ -338,7 +344,7 @@ public static string GetStringFormat(this ParameterGo parameter, string defaultF // e.g. (*fooparam).Format(rfc339Format) defaultFormat = $"({defaultFormat})"; } - + if (parameter.ModelType.IsPrimaryType(KnownPrimaryType.DateTimeRfc1123)) { return $"{defaultFormat}.In(gmt).Format(time.RFC1123)"; diff --git a/src/Templates/MarshallingXml.cshtml b/src/Templates/MarshallingXml.cshtml index f1194f994..7229c946e 100644 --- a/src/Templates/MarshallingXml.cshtml +++ b/src/Templates/MarshallingXml.cshtml @@ -40,15 +40,20 @@ @(Model.Name.FixedValue.ToShortName())Array := []@(Model.GetInterfaceName()){} for t, err := d.Token(); err == nil; t, err = d.Token() { - tt, ok := t.(xml.StartElement) - if !ok { - continue - } - @(Model.Name.FixedValue.ToShortName()), err := unmarshal@(Model.GetInterfaceName())(d, tt) + ttStart, ok := t.(xml.StartElement) + if ok { + @(Model.Name.FixedValue.ToShortName()), err := unmarshal@(Model.GetInterfaceName())(d, ttStart) if err == nil { @(Model.Name.FixedValue.ToShortName())Array = append(@(Model.Name.FixedValue.ToShortName())Array, @(Model.Name.FixedValue.ToShortName())) } } + + ttEnd, ok := t.(xml.EndElement) + if ok && start.End() == ttEnd { + break; + } + continue; + } return @(Model.Name.FixedValue.ToShortName())Array, nil } diff --git a/src/TransformerGo.cs b/src/TransformerGo.cs index 1e7229c44..2060f2349 100644 --- a/src/TransformerGo.cs +++ b/src/TransformerGo.cs @@ -86,10 +86,17 @@ private void TransformEnumTypes(CodeModelGo cmg) foreach (var et in cmg.EnumTypes) { var e = et as EnumTypeGo; - var ev = new EnumValueGo(); - ev.Name = "None"; - ev.Description = $"{EnumValueGo.FormatName(e, ev)} represents an empty {e.Name}."; + var ev = new EnumValueGo + { + Name = "None" + }; + ev.Description = $"represents an empty {e.Name}."; e.Values.Add(ev); + + foreach (var enumValue in et.Values) + { + enumValue.Name = EnumValueGo.FormatName((EnumTypeGo)et, (EnumValueGo)enumValue); + } } // And add any others with a defined name and value list (but not already located) @@ -120,9 +127,14 @@ private void TransformEnumTypes(CodeModelGo cmg) var enumValues = new List(); - var baseTypeEnumValue = new EnumValue + var typeName = $"{CodeNamerGo.Instance.GetTypeName(mt.PolymorphicDiscriminator)}"; + if (typeName.EndsWith("Type")) + { + typeName = typeName.Substring(0, typeName.Length - 4); + } + var baseTypeEnumValue = new EnumValueGo { - Name = $"{CodeNamerGo.Instance.GetTypeName(mt.PolymorphicDiscriminator)}{CodeNamerGo.Instance.GetTypeName(mt.SerializedName)}", + Name = $"{typeName}{CodeNamerGo.Instance.GetTypeName(mt.SerializedName)}", SerializedName = mt.SerializedName }; @@ -130,9 +142,9 @@ private void TransformEnumTypes(CodeModelGo cmg) foreach (var dt in mt.DerivedTypes) { - var ev = new EnumValue + var ev = new EnumValueGo { - Name = $"{CodeNamerGo.Instance.GetTypeName(mt.PolymorphicDiscriminator)}{CodeNamerGo.Instance.GetTypeName(dt.SerializedName)}", + Name = $"{typeName}{CodeNamerGo.Instance.GetTypeName(dt.SerializedName)}", SerializedName = dt.SerializedName }; enumValues.Add(ev); @@ -152,7 +164,7 @@ private void TransformEnumTypes(CodeModelGo cmg) } else { - mt.DiscriminatorEnum = cmg.Add(New(new + mt.DiscriminatorEnum = cmg.Add(New(new { Name = enumWithSameName == null ? mt.PolymorphicDiscriminator : $"{mt.PolymorphicDiscriminator}{mt.GetInterfaceName()}", Values = enumValues, @@ -167,7 +179,7 @@ private static void AssureUniqueNames(CodeModelGo cmg) // NOTE: this must be done after all enum types have been accounted for foreach (var enumType in cmg.EnumTypes) { - enumType.SetName(CodeNamerGo.Instance.GetTypeName(enumType.Name.FixedValue)); + enumType.SetName(CodeNamerGo.Instance.GetTypeName(enumType.Name)); foreach (var v in enumType.Values) { v.Name = CodeNamerGo.Instance.GetEnumMemberName(v.Name); diff --git a/test/src/tests/generated/body-string/models.go b/test/src/tests/generated/body-string/models.go index d0b89e5c0..22a6f0091 100644 --- a/test/src/tests/generated/body-string/models.go +++ b/test/src/tests/generated/body-string/models.go @@ -25,23 +25,23 @@ func joinConst(s interface{}, sep string) string { return strings.Join(ss, sep) } -// ColorsType enumerates the values for colors. +// ColorsType enumerates the values for colors type. type ColorsType string const ( - // BlueColor ... - BlueColor ColorsType = "blue_color" - // GreenColor ... - GreenColor ColorsType = "green-color" - // None ColorsNone represents an empty ColorsType. - None ColorsType = "" - // Redcolor ... - Redcolor ColorsType = "red color" + // ColorsBlueColor ... + ColorsBlueColor ColorsType = "blue_color" + // ColorsGreenColor ... + ColorsGreenColor ColorsType = "green-color" + // ColorsNone represents an empty ColorsType. + ColorsNone ColorsType = "" + // ColorsRedcolor ... + ColorsRedcolor ColorsType = "red color" ) // PossibleColorsTypeValues returns an array of possible values for the ColorsType const type. func PossibleColorsTypeValues() []ColorsType { - return []ColorsType{BlueColor, GreenColor, None, Redcolor} + return []ColorsType{ColorsBlueColor, ColorsGreenColor, ColorsNone, ColorsRedcolor} } // Error ... @@ -139,7 +139,7 @@ func (gmr GetMbcsResponse) Status() string { // GetNotExpandableResponse ... type GetNotExpandableResponse struct { rawResponse *http.Response - // Value - Possible values include: 'Redcolor', 'GreenColor', 'BlueColor', 'None' + // Value - Possible values include: 'ColorsRedcolor', 'ColorsGreenColor', 'ColorsBlueColor', 'ColorsNone' Value ColorsType `json:"value,omitempty"` } @@ -225,7 +225,7 @@ func (gnr GetNullResponse) Status() string { // GetReferencedResponse ... type GetReferencedResponse struct { rawResponse *http.Response - // Value - Possible values include: 'Redcolor', 'GreenColor', 'BlueColor', 'None' + // Value - Possible values include: 'ColorsRedcolor', 'ColorsGreenColor', 'ColorsBlueColor', 'ColorsNone' Value ColorsType `json:"value,omitempty"` } From 7a5644e0c3c154a38fb2dc9110acc550813870d9 Mon Sep 17 00:00:00 2001 From: Vlad Barosan Date: Fri, 30 Mar 2018 12:56:42 -0700 Subject: [PATCH 09/10] Fix property xml tag name --- src/Model/PropertyGo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/PropertyGo.cs b/src/Model/PropertyGo.cs index 4f2c3df1c..e760bc7ef 100644 --- a/src/Model/PropertyGo.cs +++ b/src/Model/PropertyGo.cs @@ -32,7 +32,7 @@ public string Tag(bool omitEmpty = true) bool hasParent = false; if (Parent is CompositeTypeGo go && !go.IsWrapperType) { - sb.Append(SerializedName); + sb.Append(XmlName); hasParent = true; } From 8e78dd18c246a6d53edb4434089191bf66e18b50 Mon Sep 17 00:00:00 2001 From: Vlad Barosan Date: Sun, 1 Apr 2018 23:19:02 -0700 Subject: [PATCH 10/10] remove arm- prefix from user agent string --- src/Model/CodeModelGo.cs | 2 +- test/src/tests/generated/azurereport/version.go | 2 +- test/src/tests/generated/body-boolean/version.go | 2 +- test/src/tests/generated/body-byte/version.go | 2 +- test/src/tests/generated/body-integer/version.go | 2 +- test/src/tests/generated/body-string/version.go | 2 +- test/src/tests/generated/httpinfrastructure/version.go | 2 +- test/src/tests/generated/report/version.go | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Model/CodeModelGo.cs b/src/Model/CodeModelGo.cs index 4076b5107..985e24e94 100644 --- a/src/Model/CodeModelGo.cs +++ b/src/Model/CodeModelGo.cs @@ -20,7 +20,7 @@ public class CodeModelGo : CodeModel public string Version { get; } - public string UserAgent => $"Azure-SDK-For-Go/{Version} arm-{Namespace}/{ApiVersion}"; + public string UserAgent => $"Azure-SDK-For-Go/{Version} {Namespace}/{ApiVersion}"; public CodeModelGo() { diff --git a/test/src/tests/generated/azurereport/version.go b/test/src/tests/generated/azurereport/version.go index dc02e7eb1..a8b732d98 100644 --- a/test/src/tests/generated/azurereport/version.go +++ b/test/src/tests/generated/azurereport/version.go @@ -8,7 +8,7 @@ package azurereport // UserAgent returns the UserAgent string to use when sending http.Requests. func UserAgent() string { - return "Azure-SDK-For-Go/0.0.0 arm-azurereport/1.0.0" + return "Azure-SDK-For-Go/0.0.0 azurereport/1.0.0" } // Version returns the semantic version (see http://semver.org) of the client. diff --git a/test/src/tests/generated/body-boolean/version.go b/test/src/tests/generated/body-boolean/version.go index db560104e..9d1241100 100644 --- a/test/src/tests/generated/body-boolean/version.go +++ b/test/src/tests/generated/body-boolean/version.go @@ -8,7 +8,7 @@ package booleangroup // UserAgent returns the UserAgent string to use when sending http.Requests. func UserAgent() string { - return "Azure-SDK-For-Go/0.0.0 arm-booleangroup/1.0.0" + return "Azure-SDK-For-Go/0.0.0 booleangroup/1.0.0" } // Version returns the semantic version (see http://semver.org) of the client. diff --git a/test/src/tests/generated/body-byte/version.go b/test/src/tests/generated/body-byte/version.go index 170939dfb..deecde160 100644 --- a/test/src/tests/generated/body-byte/version.go +++ b/test/src/tests/generated/body-byte/version.go @@ -8,7 +8,7 @@ package bytegroup // UserAgent returns the UserAgent string to use when sending http.Requests. func UserAgent() string { - return "Azure-SDK-For-Go/0.0.0 arm-bytegroup/1.0.0" + return "Azure-SDK-For-Go/0.0.0 bytegroup/1.0.0" } // Version returns the semantic version (see http://semver.org) of the client. diff --git a/test/src/tests/generated/body-integer/version.go b/test/src/tests/generated/body-integer/version.go index d3fabe4b4..a4c873c14 100644 --- a/test/src/tests/generated/body-integer/version.go +++ b/test/src/tests/generated/body-integer/version.go @@ -8,7 +8,7 @@ package integergroup // UserAgent returns the UserAgent string to use when sending http.Requests. func UserAgent() string { - return "Azure-SDK-For-Go/0.0.0 arm-integergroup/1.0.0" + return "Azure-SDK-For-Go/0.0.0 integergroup/1.0.0" } // Version returns the semantic version (see http://semver.org) of the client. diff --git a/test/src/tests/generated/body-string/version.go b/test/src/tests/generated/body-string/version.go index 487be7747..ea37da1b3 100644 --- a/test/src/tests/generated/body-string/version.go +++ b/test/src/tests/generated/body-string/version.go @@ -8,7 +8,7 @@ package stringgroup // UserAgent returns the UserAgent string to use when sending http.Requests. func UserAgent() string { - return "Azure-SDK-For-Go/0.0.0 arm-stringgroup/1.0.0" + return "Azure-SDK-For-Go/0.0.0 stringgroup/1.0.0" } // Version returns the semantic version (see http://semver.org) of the client. diff --git a/test/src/tests/generated/httpinfrastructure/version.go b/test/src/tests/generated/httpinfrastructure/version.go index 5aab890e7..15da85766 100644 --- a/test/src/tests/generated/httpinfrastructure/version.go +++ b/test/src/tests/generated/httpinfrastructure/version.go @@ -8,7 +8,7 @@ package httpinfrastructuregroup // UserAgent returns the UserAgent string to use when sending http.Requests. func UserAgent() string { - return "Azure-SDK-For-Go/0.0.0 arm-httpinfrastructuregroup/1.0.0" + return "Azure-SDK-For-Go/0.0.0 httpinfrastructuregroup/1.0.0" } // Version returns the semantic version (see http://semver.org) of the client. diff --git a/test/src/tests/generated/report/version.go b/test/src/tests/generated/report/version.go index 8f3813734..c1fd411d4 100644 --- a/test/src/tests/generated/report/version.go +++ b/test/src/tests/generated/report/version.go @@ -8,7 +8,7 @@ package report // UserAgent returns the UserAgent string to use when sending http.Requests. func UserAgent() string { - return "Azure-SDK-For-Go/0.0.0 arm-report/1.0.0" + return "Azure-SDK-For-Go/0.0.0 report/1.0.0" } // Version returns the semantic version (see http://semver.org) of the client.