diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/AutoRest.Generator.AzureResourceSchema.Tests.csproj b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/AutoRest.Generator.AzureResourceSchema.Tests.csproj index 242444dbc8c08..dda8fa1fd6407 100644 --- a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/AutoRest.Generator.AzureResourceSchema.Tests.csproj +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/AutoRest.Generator.AzureResourceSchema.Tests.csproj @@ -34,6 +34,10 @@ 4 + + ..\..\..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + True + diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/AzureResourceSchemaCodeGeneratorTests.cs b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/AzureResourceSchemaCodeGeneratorTests.cs index 4591537496b3e..c5c37c0a235f2 100644 --- a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/AzureResourceSchemaCodeGeneratorTests.cs +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/AzureResourceSchemaCodeGeneratorTests.cs @@ -2,7 +2,10 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using Microsoft.Rest.Generator.ClientModel; -using System; +using Microsoft.Rest.Generator.Utilities; +using Newtonsoft.Json.Linq; +using System.Linq; +using System.Threading.Tasks; using Xunit; namespace Microsoft.Rest.Generator.AzureResourceSchema.Tests @@ -11,46 +14,142 @@ namespace Microsoft.Rest.Generator.AzureResourceSchema.Tests public class AzureResourceSchemaCodeGeneratorTests { [Fact] - public void DescriptionThrowsException() + public void Description() { Assert.Equal("Azure Resource Schema generator", CreateGenerator().Description); } [Fact] - public void ImplementationFileExtensionThrowsException() + public void ImplementationFileExtension() { Assert.Equal(".json", CreateGenerator().ImplementationFileExtension); } [Fact] - public void NameThrowsException() + public void Name() { Assert.Equal("AzureResourceSchema", CreateGenerator().Name); } [Fact] - public void UsageInstructionsThrowsException() + public void UsageInstructionsWithNoOutputFileSetting() { - Assert.Equal("MOCK USAGE INSTRUCTIONS", CreateGenerator().UsageInstructions); + AzureResourceSchemaCodeGenerator codeGen = CreateGenerator(); + Assert.Equal("Your Azure Resource Schema can be found at " + codeGen.SchemaPath, codeGen.UsageInstructions); } [Fact] - public void GenerateThrowsException() + public void UsageInstructionsWithOutputFileSetting() { - ServiceClient serviceClient = new ServiceClient(); - Assert.Throws(() => { CreateGenerator().Generate(serviceClient); }); + Settings settings = new Settings() + { + OutputFileName = "spam.json" + }; + AzureResourceSchemaCodeGenerator codeGen = CreateGenerator(settings); + + Assert.Equal("Your Azure Resource Schema can be found at " + codeGen.SchemaPath, codeGen.UsageInstructions); + } + + [Fact] + public async void GenerateWithEmptyServiceClient() + { + await TestGenerate(new string[0], + @"{ + 'id': 'http://schema.management.azure.com/schemas//Microsoft.Storage.json#', + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Microsoft.Storage', + 'description': 'Microsoft Storage Resource Types', + 'resourceDefinitions': { } + }"); + } + + [Fact] + public async void GenerateWithServiceClientWithOneType() + { + await TestGenerate(new string[] + { + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Mock.Provider/mockType" + }, + @"{ + 'id': 'http://schema.management.azure.com/schemas//Microsoft.Storage.json#', + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Microsoft.Storage', + 'description': 'Microsoft Storage Resource Types', + 'resourceDefinitions': { + 'mockType': { + } + } + }"); + } + + [Fact] + public async void GenerateWithServiceClientWithTwoTypes() + { + await TestGenerate(new string[] + { + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Mock.Provider/mockType1", + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Mock.Provider/mockType2" + }, + @"{ + 'id': 'http://schema.management.azure.com/schemas//Microsoft.Storage.json#', + '$schema': 'http://json-schema.org/draft-04/schema#', + 'title': 'Microsoft.Storage', + 'description': 'Microsoft Storage Resource Types', + 'resourceDefinitions': { + 'mockType1': { + }, + 'mockType2': { + } + } + }"); } [Fact] - public void NormalizeClientModelThrowsException() + public void NormalizeClientModelDoesNothing() { ServiceClient serviceClient = new ServiceClient(); - Assert.Throws(() => { CreateGenerator().NormalizeClientModel(serviceClient); }); + CreateGenerator().NormalizeClientModel(serviceClient); + + // Nothing happens } private static AzureResourceSchemaCodeGenerator CreateGenerator() { - return new AzureResourceSchemaCodeGenerator(new Settings()); + return CreateGenerator(new Settings()); + } + private static AzureResourceSchemaCodeGenerator CreateGenerator(Settings settings) + { + return new AzureResourceSchemaCodeGenerator(settings); + } + + private static async Task TestGenerate(string[] methodUrls, string expectedJsonString) + { + MemoryFileSystem fileSystem = new MemoryFileSystem(); + + Settings settings = new Settings(); + settings.FileSystem = fileSystem; + + ServiceClient serviceClient = new ServiceClient(); + foreach(string methodUrl in methodUrls) + { + serviceClient.Methods.Add(new Method() + { + Url = methodUrl + }); + } + await CreateGenerator(settings).Generate(serviceClient); + + Assert.Equal(2, fileSystem.VirtualStore.Count); + + string folderPath = fileSystem.VirtualStore.Keys.First(); + Assert.Equal("Folder", fileSystem.VirtualStore[folderPath].ToString()); + + JObject expectedJSON = JObject.Parse(expectedJsonString); + + string fileContents = fileSystem.VirtualStore[fileSystem.VirtualStore.Keys.Skip(1).First()].ToString(); + JObject actualJson = JObject.Parse(fileContents); + + Assert.Equal(expectedJSON, actualJson); } } } diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/packages.config b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/packages.config index 64fb5d7b3b792..c2c499b747eb4 100644 --- a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/packages.config +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/packages.config @@ -1,5 +1,6 @@  + diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AutoRest.Generator.AzureResourceSchema.csproj b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AutoRest.Generator.AzureResourceSchema.csproj index f934e945c6e9e..5b00abb1fff44 100644 --- a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AutoRest.Generator.AzureResourceSchema.csproj +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AutoRest.Generator.AzureResourceSchema.csproj @@ -20,6 +20,10 @@ 4 + + ..\..\..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + True + @@ -44,5 +48,8 @@ CustomDictionary.xml + + + \ No newline at end of file diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AzureResourceSchemaCodeGenerator.cs b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AzureResourceSchemaCodeGenerator.cs index e879d0b9bd2f6..5d5df37291ca7 100644 --- a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AzureResourceSchemaCodeGenerator.cs +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AzureResourceSchemaCodeGenerator.cs @@ -2,18 +2,33 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using Microsoft.Rest.Generator.ClientModel; +using Newtonsoft.Json; using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Threading.Tasks; namespace Microsoft.Rest.Generator.AzureResourceSchema { public class AzureResourceSchemaCodeGenerator : CodeGenerator { + private const string resourceMethodPrefix = "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/"; + public AzureResourceSchemaCodeGenerator(Settings settings) : base(settings) { } + public string SchemaPath + { + get + { + string defaultSchemaFileName = Path.GetFileNameWithoutExtension(Settings.Input) + ".schema.json"; + return Path.Combine(Settings.OutputDirectory, Settings.OutputFileName ?? defaultSchemaFileName); + } + } + public override string Description { get { return "Azure Resource Schema generator"; } @@ -31,17 +46,145 @@ public override string Name public override string UsageInstructions { - get { return "MOCK USAGE INSTRUCTIONS"; } + get { return "Your Azure Resource Schema can be found at " + SchemaPath; } } - public override Task Generate(ServiceClient serviceClient) + public override void NormalizeClientModel(ServiceClient serviceClient) { - throw new NotImplementedException(); } - public override void NormalizeClientModel(ServiceClient serviceClient) + public override async Task Generate(ServiceClient serviceClient) { - throw new NotImplementedException(); + StringWriter stringWriter = new StringWriter(); + using (JsonTextWriter writer = new JsonTextWriter(stringWriter)) + { + writer.Formatting = Formatting.Indented; + writer.Indentation = 2; + writer.IndentChar = ' '; + + string resourceProvider = GetResourceProvider(serviceClient); + IEnumerable resourceFullTypes = GetResourceFullTypes(serviceClient); + + WriteObject(writer, () => { + WriteProperty(writer, "id", string.Format("http://schema.management.azure.com/schemas/{0}/Microsoft.Storage.json#", serviceClient.ApiVersion)); + WriteProperty(writer, "$schema", "http://json-schema.org/draft-04/schema#"); + WriteProperty(writer, "title", resourceProvider); + WriteProperty(writer, "description", resourceProvider.Replace('.', ' ') + " Resource Types"); + WriteProperty(writer, "resourceDefinitions", () => { + foreach (string resourceFullType in resourceFullTypes) + { + string resourceShortType = resourceFullType.Substring(resourceFullType.IndexOf('/') + 1); + + WriteProperty(writer, resourceShortType, () => { + WriteProperty(writer, "type", "object"); + WriteProperty(writer, "properties", () => { + WriteProperty(writer, "type", () => { + WriteProperty(writer, "enum", new string[] { + resourceFullType + }); + }); + WriteProperty(writer, "apiVersion", () => { + WriteProperty(writer, "enum", new string[] { + serviceClient.ApiVersion + }); + }); + WriteProperty(writer, "properties", () => { + WriteProperty(writer, "type", "object"); + WriteProperty(writer, "properties", () => { + }); + WriteProperty(writer, "required", new string[0]); + }); + }); + WriteProperty(writer, "required", new string[] { + "type", + "apiVersion", + "properties", + "location" + }); + WriteProperty(writer, "description", resourceFullType); + }); + } + }); + }); + } + + await Write(stringWriter.ToString(), SchemaPath); + } + + private static IEnumerable GetResourceMethods(ServiceClient serviceClient) + { + return GetResourceMethods(serviceClient.Methods); + } + + private static IEnumerable GetResourceMethods(IEnumerable methods) + { + return methods.Where(m => m.Url.StartsWith(resourceMethodPrefix)); + } + + private static IEnumerable GetResourceMethodUrisAfterPrefix(ServiceClient serviceClient) + { + IEnumerable resourceMethods = GetResourceMethods(serviceClient); + IEnumerable resourceMethodUris = resourceMethods.Select(rm => rm.Url); + return resourceMethodUris.Select(rmu => rmu.Substring(resourceMethodPrefix.Length)); + } + + private static string GetResourceProvider(ServiceClient serviceClient) + { + IEnumerable resourceMethodUrisAfterPrefix = GetResourceMethodUrisAfterPrefix(serviceClient); + return resourceMethodUrisAfterPrefix.Select(rmuap => rmuap.Substring(0, rmuap.IndexOf('/'))).Distinct().Single(); + } + + private static IEnumerable GetResourceFullTypes(ServiceClient serviceClient) + { + IEnumerable resourceMethodUrisAfterPrefix = GetResourceMethodUrisAfterPrefix(serviceClient); + return resourceMethodUrisAfterPrefix.Select(rmuap => + { + int forwardSlashAfterProvider = rmuap.IndexOf('/'); + int forwardSlashAfterType = rmuap.IndexOf('/', forwardSlashAfterProvider + 1); + int startIndex = forwardSlashAfterProvider + 1; + if (forwardSlashAfterType == -1) + { + return rmuap; + } + else + { + return rmuap.Substring(0, forwardSlashAfterType); + } + }).Distinct(); + } + + private static void WriteObject(JsonTextWriter writer, Action writeObjectContents) + { + writer.WriteStartObject(); + + writeObjectContents.Invoke(); + + writer.WriteEndObject(); + } + + private static void WriteProperty(JsonTextWriter writer, string propertyName, string propertyValue) + { + writer.WritePropertyName(propertyName); + writer.WriteValue(propertyValue); + } + + private static void WriteProperty(JsonTextWriter writer, string propertyName, Action writeObjectContents) + { + writer.WritePropertyName(propertyName); + WriteObject(writer, writeObjectContents); + } + + private static void WriteProperty(JsonTextWriter writer, string propertyName, string[] writeArrayContents) + { + writer.WritePropertyName(propertyName); + writer.WriteStartArray(); + + foreach (string value in writeArrayContents) + { + writer.WriteValue(value); + } + + writer.WriteEndArray(); } } } diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/packages.config b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/packages.config new file mode 100644 index 0000000000000..505e58836baea --- /dev/null +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file