From 31e5a5214bdab6d47a02cab4782bea4042f085b9 Mon Sep 17 00:00:00 2001 From: Dr Freon Date: Fri, 10 Jun 2016 17:20:05 -0400 Subject: [PATCH] Supply CodeGenSettings via a JSON file to AutoRest. CodeGenSettings is de-serialized as Dictionary so it can contain more complex properties than just strings. (#1147) It passed the CI check we ran on another node. One CI servers is hanging alot. --- .../AutoRest.Core.Tests.csproj | 3 ++ .../AutoRestSettingsTests.cs | 25 ++++++++- .../Resource/SampleSettings.json | 8 +++ AutoRest/AutoRest.Core/CodeGenerator.cs | 2 +- .../Properties/Resources.Designer.cs | 9 ++++ .../AutoRest.Core/Properties/Resources.resx | 3 ++ AutoRest/AutoRest.Core/Settings.cs | 51 ++++++++++++++++--- .../Azure.CSharp/AzureCSharpCodeNamer.cs | 2 +- .../CSharp/CSharp/CSharpCodeGenerator.cs | 2 +- .../Generators/Ruby/Ruby/RubyCodeGenerator.cs | 2 +- AutoRest/Modelers/Swagger/SwaggerModeler.cs | 4 +- Documentation/cli.md | 7 +-- Documentation/swagger-extensions.md | 3 +- 13 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 AutoRest/AutoRest.Core.Tests/Resource/SampleSettings.json diff --git a/AutoRest/AutoRest.Core.Tests/AutoRest.Core.Tests.csproj b/AutoRest/AutoRest.Core.Tests/AutoRest.Core.Tests.csproj index cabe0eda3b4ee..e19fdb0d8c5af 100644 --- a/AutoRest/AutoRest.Core.Tests/AutoRest.Core.Tests.csproj +++ b/AutoRest/AutoRest.Core.Tests/AutoRest.Core.Tests.csproj @@ -88,6 +88,9 @@ PreserveNewest + + Always + diff --git a/AutoRest/AutoRest.Core.Tests/AutoRestSettingsTests.cs b/AutoRest/AutoRest.Core.Tests/AutoRestSettingsTests.cs index 0b8d7dc34a871..f11f448205c3b 100644 --- a/AutoRest/AutoRest.Core.Tests/AutoRestSettingsTests.cs +++ b/AutoRest/AutoRest.Core.Tests/AutoRestSettingsTests.cs @@ -1,9 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection; using Microsoft.Rest.Generator.Logging; +using Newtonsoft.Json.Linq; using Xunit; namespace Microsoft.Rest.Generator.Test @@ -16,11 +20,11 @@ public void CreateWithoutArgumentsReturnsBlankSettings() { var settings = Settings.Create((string[]) null); Assert.NotNull(settings); - settings = Settings.Create((IDictionary) null); + settings = Settings.Create((IDictionary) null); Assert.NotNull(settings); settings = Settings.Create(new string[0]); Assert.NotNull(settings); - settings = Settings.Create(new Dictionary()); + settings = Settings.Create(new Dictionary()); Assert.NotNull(settings); } @@ -33,6 +37,23 @@ public void CreateWithMultipleEmptyKeysStoreInCustomDictonary() Assert.Equal("", settings.CustomSettings["Bar"]); } + [Fact] + public void LoadCodeGenSettingsFromJsonFile() + { + var codeBaseUrl = new Uri(Assembly.GetExecutingAssembly().CodeBase); + var codeBasePath = Uri.UnescapeDataString(codeBaseUrl.AbsolutePath); + var dirPath = Path.GetDirectoryName(codeBasePath); + var settingsFile = Path.Combine(dirPath, "Resource\\SampleSettings.json"); + var settings = Settings.Create(new[] {"-cgs", settingsFile}); + Assert.False((bool) settings.CustomSettings["sampleSwitchFalse"]); + Assert.True((bool) settings.CustomSettings["sampleSwitchTrue"]); + Assert.Equal("Foo", settings.CustomSettings["sampleString"]); + Assert.Equal(typeof (JArray), settings.CustomSettings["filePathArray"].GetType()); + Assert.Equal(2, ((JArray) settings.CustomSettings["filePathArray"]).Count); + Assert.Equal(typeof (JArray), settings.CustomSettings["intArray"].GetType()); + Assert.Equal(typeof (long), settings.CustomSettings["intFoo"].GetType()); + } + [Fact] public void EmptyCredentialsSettingIsSetToTrueIfPassed() { diff --git a/AutoRest/AutoRest.Core.Tests/Resource/SampleSettings.json b/AutoRest/AutoRest.Core.Tests/Resource/SampleSettings.json new file mode 100644 index 0000000000000..6ee036cba2536 --- /dev/null +++ b/AutoRest/AutoRest.Core.Tests/Resource/SampleSettings.json @@ -0,0 +1,8 @@ +{ + "sampleSwitchFalse": false, + "sampleSwitchTrue": true, + "sampleString": "Foo", + "filePathArray": [ "C:\\Foo.dll", "C:\\Bar.dll" ], + "intArray": [ 1, 2, 3 ], + "intFoo": 5 +} diff --git a/AutoRest/AutoRest.Core/CodeGenerator.cs b/AutoRest/AutoRest.Core/CodeGenerator.cs index 57dbecb697346..55e20dee7c539 100644 --- a/AutoRest/AutoRest.Core/CodeGenerator.cs +++ b/AutoRest/AutoRest.Core/CodeGenerator.cs @@ -56,7 +56,7 @@ public virtual string HeaderFileExtension /// Populate settings on self and any child objects /// /// A dictionary of settings - public virtual void PopulateSettings(IDictionary settings) + public virtual void PopulateSettings(IDictionary settings) { Settings.PopulateSettings(this, settings); } diff --git a/AutoRest/AutoRest.Core/Properties/Resources.Designer.cs b/AutoRest/AutoRest.Core/Properties/Resources.Designer.cs index 2ef7dbdc586ae..975a7983ee085 100644 --- a/AutoRest/AutoRest.Core/Properties/Resources.Designer.cs +++ b/AutoRest/AutoRest.Core/Properties/Resources.Designer.cs @@ -78,6 +78,15 @@ internal static string CodeGenerationFailed { } } + /// + /// Looks up a localized string similar to Could not load CodeGenSettings file '{0}'. Exception: '{1}'.. + /// + internal static string CodeGenSettingsFileInvalid { + get { + return ResourceManager.GetString("CodeGenSettingsFileInvalid", resourceCulture); + } + } + /// /// Looks up a localized string similar to \\\\. /// diff --git a/AutoRest/AutoRest.Core/Properties/Resources.resx b/AutoRest/AutoRest.Core/Properties/Resources.resx index 18688acae3326..17f3fe59fa18b 100644 --- a/AutoRest/AutoRest.Core/Properties/Resources.resx +++ b/AutoRest/AutoRest.Core/Properties/Resources.resx @@ -123,6 +123,9 @@ Code generation failed with errors. See inner exceptions for details. + + Could not load CodeGenSettings file '{0}'. Exception: '{1}'. + \\\\ diff --git a/AutoRest/AutoRest.Core/Settings.cs b/AutoRest/AutoRest.Core/Settings.cs index 63675cf0e46b4..81ea5ca05c3df 100644 --- a/AutoRest/AutoRest.Core/Settings.cs +++ b/AutoRest/AutoRest.Core/Settings.cs @@ -10,6 +10,8 @@ using Microsoft.Rest.Generator.Properties; using Microsoft.Rest.Generator.Utilities; using System.Globalization; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Microsoft.Rest.Generator { @@ -46,7 +48,7 @@ public Settings() { FileSystem = new FileSystem(); OutputDirectory = Path.Combine(Environment.CurrentDirectory, "Generated"); - CustomSettings = new Dictionary(StringComparer.OrdinalIgnoreCase); + CustomSettings = new Dictionary(StringComparer.OrdinalIgnoreCase); Header = string.Format(CultureInfo.InvariantCulture, DefaultCodeGenerationHeader, AutoRest.Version); CodeGenerator = "CSharp"; Modeler = "Swagger"; @@ -60,7 +62,7 @@ public Settings() /// /// Custom provider specific settings. /// - public IDictionary CustomSettings { get; private set; } + public IDictionary CustomSettings { get; private set; } // The CommandLineInfo attribute is reflected to display help. // Prefer to show required properties before optional. @@ -215,6 +217,9 @@ public string Header [SettingsInfo("Package version of then generated code package. Should be then version wanted for the package in then package manager.")] public string PackageVersion { get; set; } + [SettingsAlias("cgs")] + [SettingsInfo("The path for a json file containing code generation settings.")] + public string CodeGenSettings { get; set; } /// /// Factory method to generate CodeGenerationSettings from command line arguments. /// Matches dictionary keys to the settings properties. @@ -223,7 +228,7 @@ public string Header /// CodeGenerationSettings public static Settings Create(string[] arguments) { - var argsDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + var argsDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (arguments != null && arguments.Length > 0) { string key = null; @@ -257,7 +262,7 @@ public static Settings Create(string[] arguments) return Create(argsDictionary); } - private static void AddArgumentToDictionary(string key, string value, Dictionary argsDictionary) + private static void AddArgumentToDictionary(string key, string value, IDictionary argsDictionary) { key = key ?? "Default"; value = value ?? String.Empty; @@ -270,7 +275,7 @@ private static void AddArgumentToDictionary(string key, string value, Dictionary /// /// Dictionary of settings /// Settings - public static Settings Create(IDictionary settings) + public static Settings Create(IDictionary settings) { var autoRestSettings = new Settings(); if (settings == null || settings.Count == 0) @@ -281,6 +286,16 @@ public static Settings Create(IDictionary settings) PopulateSettings(autoRestSettings, settings); autoRestSettings.CustomSettings = settings; + if (!string.IsNullOrEmpty(autoRestSettings.CodeGenSettings)) + { + var settingsContent = autoRestSettings.FileSystem.ReadFileAsText(autoRestSettings.CodeGenSettings); + var codeGenSettingsDictionary = + JsonConvert.DeserializeObject>(settingsContent); + foreach (var pair in codeGenSettingsDictionary) + { + autoRestSettings.CustomSettings[pair.Key] = pair.Value; + } + } return autoRestSettings; } @@ -290,7 +305,7 @@ public static Settings Create(IDictionary settings) /// Object to populate from dictionary. /// Dictionary of settings.Settings that are populated get removed from the dictionary. /// Dictionary of settings that were not matched. - public static void PopulateSettings(object entityToPopulate, IDictionary settings) + public static void PopulateSettings(object entityToPopulate, IDictionary settings) { if (entityToPopulate == null) { @@ -311,13 +326,33 @@ public static void PopulateSettings(object entityToPopulate, IDictionary c.ToString()) + .ToArray(); + + property.SetValue(entityToPopulate, stringArray); + } + else if (elementType == typeof (int)) + { + var intValues = ((JArray)setting.Value).Children(). + Select(c => (int)Convert.ChangeType(c, elementType, CultureInfo.InvariantCulture)) + .ToArray(); + property.SetValue(entityToPopulate, intValues); + } } else { diff --git a/AutoRest/Generators/CSharp/Azure.CSharp/AzureCSharpCodeNamer.cs b/AutoRest/Generators/CSharp/Azure.CSharp/AzureCSharpCodeNamer.cs index 2fb396c256361..2b5f005ed0323 100644 --- a/AutoRest/Generators/CSharp/Azure.CSharp/AzureCSharpCodeNamer.cs +++ b/AutoRest/Generators/CSharp/Azure.CSharp/AzureCSharpCodeNamer.cs @@ -33,7 +33,7 @@ public AzureCSharpCodeNamer(Settings settings) if (setting.Equals("useDateTimeOffset", StringComparison.OrdinalIgnoreCase)) { bool toUse; - if (bool.TryParse(settings.CustomSettings[setting], out toUse)) + if (bool.TryParse(settings.CustomSettings[setting].ToString(), out toUse)) { UseDateTimeOffset = toUse; } diff --git a/AutoRest/Generators/CSharp/CSharp/CSharpCodeGenerator.cs b/AutoRest/Generators/CSharp/CSharp/CSharpCodeGenerator.cs index 5b3ba7981eb0c..8834f4c010834 100644 --- a/AutoRest/Generators/CSharp/CSharp/CSharpCodeGenerator.cs +++ b/AutoRest/Generators/CSharp/CSharp/CSharpCodeGenerator.cs @@ -60,7 +60,7 @@ public override string ImplementationFileExtension get { return ".cs"; } } - public override void PopulateSettings(IDictionary settings) + public override void PopulateSettings(IDictionary settings) { base.PopulateSettings(settings); Settings.PopulateSettings(_namer, settings); diff --git a/AutoRest/Generators/Ruby/Ruby/RubyCodeGenerator.cs b/AutoRest/Generators/Ruby/Ruby/RubyCodeGenerator.cs index 368888b082657..114540a6c5372 100644 --- a/AutoRest/Generators/Ruby/Ruby/RubyCodeGenerator.cs +++ b/AutoRest/Generators/Ruby/Ruby/RubyCodeGenerator.cs @@ -58,7 +58,7 @@ public RubyCodeGenerator(Settings settings) : base(settings) if (Settings.CustomSettings.ContainsKey("Name")) { - this.sdkName = Settings.CustomSettings["Name"]; + this.sdkName = Settings.CustomSettings["Name"].ToString(); } if (sdkName == null) diff --git a/AutoRest/Modelers/Swagger/SwaggerModeler.cs b/AutoRest/Modelers/Swagger/SwaggerModeler.cs index 9a009c4a77d99..829fbc174cad2 100644 --- a/AutoRest/Modelers/Swagger/SwaggerModeler.cs +++ b/AutoRest/Modelers/Swagger/SwaggerModeler.cs @@ -144,7 +144,9 @@ private void UpdateSettings() { foreach (var key in ServiceDefinition.Info.CodeGenerationSettings.Extensions.Keys) { - this.Settings.CustomSettings[key] = ServiceDefinition.Info.CodeGenerationSettings.Extensions[key].ToString(); + //Don't overwrite settings that come in from the command line + if (!this.Settings.CustomSettings.ContainsKey(key)) + this.Settings.CustomSettings[key] = ServiceDefinition.Info.CodeGenerationSettings.Extensions[key]; } Settings.PopulateSettings(this.Settings, this.Settings.CustomSettings); } diff --git a/Documentation/cli.md b/Documentation/cli.md index 4dddaeec8a919..ee9159e5f39d9 100644 --- a/Documentation/cli.md +++ b/Documentation/cli.md @@ -25,7 +25,8 @@ **-OutputFileName** If set, will cause generated code to be output to a single file. Not supported by all code generators. **-Verbose** If set, will output verbose diagnostic messages. - + + **-CodeGenSettings** Optionally specifies a JSON file that contains code generation settings equivalent to the swagger document containing the same content in the [x-ms-code-generation-settings] extension. Aliases: -cgs ##Code Generators **Ruby** Generic Ruby code generator. @@ -68,7 +69,7 @@ AutoRest.exe -Namespace MyNamespace -Input swagger.json AutoRest.exe -Namespace MyNamespace -Header "Copyright Contoso Ltd" -Input swagger.json ``` - - Generate C# client with a credentials property in MyNamespace from swagger.json input: + - Generate C# client with a credentials property in MyNamespace from swagger.json input and with code generation settings specified by settings.json: ```bash -AutoRest.exe -AddCredentials true -Namespace MyNamespace -CodeGenerator CSharp -Modeler Swagger -Input swagger.json +AutoRest.exe -AddCredentials true -Namespace MyNamespace -CodeGenerator CSharp -Modeler Swagger -Input swagger.json -CodeGenSettings settings.json ``` diff --git a/Documentation/swagger-extensions.md b/Documentation/swagger-extensions.md index 1fbf0efe46d71..af5ab33f28f9b 100644 --- a/Documentation/swagger-extensions.md +++ b/Documentation/swagger-extensions.md @@ -39,7 +39,8 @@ Field Name | Type | Description "info": { "x-ms-code-generation-settings": { "header": "MIT", - "internalConstructors": true + "internalConstructors": true, + "useDateTimeOffset": true } } ```