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 9be4197827..be1ade26d4 100644 --- a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/AutoRest.Generator.AzureResourceSchema.Tests.csproj +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema.Tests/AutoRest.Generator.AzureResourceSchema.Tests.csproj @@ -47,21 +47,6 @@ - - ..\..\..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll - True - - - ..\..\..\..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll - True - - - ..\..\..\..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll - True - - - ..\..\..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll - ..\..\..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll True diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AutoRest.Generator.AzureResourceSchema.csproj b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AutoRest.Generator.AzureResourceSchema.csproj index 5385b9c337..e7549af2d2 100644 --- a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AutoRest.Generator.AzureResourceSchema.csproj +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AutoRest.Generator.AzureResourceSchema.csproj @@ -30,6 +30,8 @@ + + Properties\AssemblyVersionInfo.cs @@ -47,7 +49,7 @@ - CustomDictionary.xml + CustomDictionary.xml @@ -81,6 +83,7 @@ + diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AzureResourceSchemaCodeGenerator.cs b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AzureResourceSchemaCodeGenerator.cs index 69d8deb74c..ed8daccd72 100644 --- a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AzureResourceSchemaCodeGenerator.cs +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/AzureResourceSchemaCodeGenerator.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -13,8 +14,6 @@ namespace Microsoft.Rest.Generator.AzureResourceSchema { public class AzureResourceSchemaCodeGenerator : CodeGenerator { - private const string resourceMethodPrefix = "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/"; - public AzureResourceSchemaCodeGenerator(Settings settings) : base(settings) { @@ -55,102 +54,111 @@ public override void NormalizeClientModel(ServiceClient serviceClient) public override async Task Generate(ServiceClient serviceClient) { - StringWriter stringWriter = new StringWriter(); - using (JsonTextWriter writer = new JsonTextWriter(stringWriter)) + try { - 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}/{1}.json#", serviceClient.ApiVersion, resourceProvider)); - 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); - }); - } - }); - }); - } + StringWriter stringWriter = new StringWriter(); + using (JsonTextWriter writer = new JsonTextWriter(stringWriter)) + { + writer.Formatting = Formatting.Indented; + writer.Indentation = 2; + writer.IndentChar = ' '; - await Write(stringWriter.ToString(), SchemaPath); - } + Schema schema = Schema.Parse(serviceClient); - private static IEnumerable GetResourceMethods(ServiceClient serviceClient) - { - return GetResourceMethods(serviceClient.Methods); - } + WriteSchema(writer, schema); + } - private static IEnumerable GetResourceMethods(IEnumerable methods) - { - return methods.Where(m => m.Url.StartsWith(resourceMethodPrefix)); + await Write(stringWriter.ToString(), SchemaPath); + } + catch (Exception) + { + Debugger.Break(); + } } - private static IEnumerable GetResourceMethodUrisAfterPrefix(ServiceClient serviceClient) + private static void WriteSchema(JsonTextWriter writer, Schema schema) { - IEnumerable resourceMethods = GetResourceMethods(serviceClient); - IEnumerable resourceMethodUris = resourceMethods.Select(rm => rm.Url); - return resourceMethodUris.Select(rmu => rmu.Substring(resourceMethodPrefix.Length)); + WriteObject(writer, () => + { + WriteProperty(writer, "id", schema.Id); + WriteProperty(writer, "$schema", schema.SchemaUri); + WriteProperty(writer, "title", schema.Title); + WriteProperty(writer, "description", schema.Description); + WriteProperty(writer, "resourceDefinitions", () => + { + foreach (Resource resource in schema.Resources) + { + WriteResource(writer, resource); + } + }); + }); } - private static string GetResourceProvider(ServiceClient serviceClient) + private static void WriteResource(JsonTextWriter writer, Resource resource) { - IEnumerable resourceMethodUrisAfterPrefix = GetResourceMethodUrisAfterPrefix(serviceClient); - return resourceMethodUrisAfterPrefix.Select(rmuap => rmuap.Substring(0, rmuap.IndexOf('/'))).Distinct().Single(); + WriteProperty(writer, resource.Name, () => + { + WriteProperty(writer, "type", "object"); + WriteProperty(writer, "properties", () => + { + WriteProperty(writer, "type", () => + { + WriteProperty(writer, "enum", new string[] + { + resource.Type + }); + }); + + WriteProperty(writer, "apiVersion", () => + { + WriteProperty(writer, "enum", resource.ApiVersions); + }); + + WriteProperty(writer, "properties", () => + { + WriteObjectOrExpression(writer, () => + { + WriteProperty(writer, "type", "object"); + WriteProperty(writer, "properties", () => + { + foreach (ResourceProperty property in resource.Properties) + { + WriteResourceProperty(writer, property); + } + }); + WriteProperty(writer, "required", resource.RequiredPropertyNames); + }); + }); + }); + WriteProperty(writer, "required", new string[] + { + "type", + "apiVersion", + "properties", + "location" + }); + WriteProperty(writer, "description", resource.Description); + }); } - private static IEnumerable GetResourceFullTypes(ServiceClient serviceClient) + private static void WriteResourceProperty(JsonTextWriter writer, ResourceProperty resourceProperty) { - IEnumerable resourceMethodUrisAfterPrefix = GetResourceMethodUrisAfterPrefix(serviceClient); - return resourceMethodUrisAfterPrefix.Select(rmuap => + WriteProperty(writer, resourceProperty.Name, () => { - int forwardSlashAfterProvider = rmuap.IndexOf('/'); - int forwardSlashAfterType = rmuap.IndexOf('/', forwardSlashAfterProvider + 1); - int startIndex = forwardSlashAfterProvider + 1; - if (forwardSlashAfterType == -1) + WriteObjectOrExpression(writer, () => { - return rmuap; - } - else - { - return rmuap.Substring(0, forwardSlashAfterType); - } - }).Distinct(); + if (resourceProperty.Type != null) + { + WriteProperty(writer, "type", resourceProperty.Type); + } + + if (resourceProperty.AllowedValues != null) + { + WriteProperty(writer, "allowedValues", resourceProperty.AllowedValues); + } + }); + WriteProperty(writer, "description", resourceProperty.Description); + }); } private static void WriteObject(JsonTextWriter writer, Action writeObjectContents) @@ -162,19 +170,45 @@ private static void WriteObject(JsonTextWriter writer, Action writeObjectContent writer.WriteEndObject(); } + private static void WriteExpressionReference(JsonTextWriter writer) + { + WriteObject(writer, () => + { + WriteProperty(writer, "$ref", "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"); + }); + } + + private static void WriteObjectOrExpression(JsonTextWriter writer, Action writeObjectContents) + { + writer.WritePropertyName("oneOf"); + writer.WriteStartArray(); + + WriteObject(writer, writeObjectContents); + + WriteExpressionReference(writer); + + writer.WriteEndArray(); + } + private static void WriteProperty(JsonTextWriter writer, string propertyName, string propertyValue) { writer.WritePropertyName(propertyName); writer.WriteValue(propertyValue); } + private static void WriteProperty(JsonTextWriter writer, string propertyName, int 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) + private static void WriteProperty(JsonTextWriter writer, string propertyName, IEnumerable writeArrayContents) { writer.WritePropertyName(propertyName); writer.WriteStartArray(); diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/Resource.cs b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/Resource.cs new file mode 100644 index 0000000000..fc1a67175c --- /dev/null +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/Resource.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Rest.Generator.AzureResourceSchema +{ + public class Resource + { + private readonly string name; + private readonly string type; + private readonly string[] apiVersions; + private readonly IEnumerable properties; + private readonly string description; + + public Resource(string name, string type, string[] apiVersions, IEnumerable properties, string description) + { + this.name = name; + this.type = type; + this.apiVersions = apiVersions; + this.properties = properties; + this.description = description; + } + + public string Name + { + get { return name; } + } + + public string Type + { + get { return type; } + } + + public string[] ApiVersions + { + get { return apiVersions; } + } + + public IEnumerable Properties + { + get { return properties; } + } + + public string[] RequiredPropertyNames + { + get { return Properties.Where(property => property.IsRequired).Select(property => property.Name).ToArray(); } + } + + public string Description + { + get { return description; } + } + } +} diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/ResourceProperty.cs b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/ResourceProperty.cs new file mode 100644 index 0000000000..ecb0c10a6b --- /dev/null +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/ResourceProperty.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +namespace Microsoft.Rest.Generator.AzureResourceSchema +{ + public class ResourceProperty + { + private readonly string name; + private readonly bool isRequired; + private readonly string type; + private readonly string[] allowedValues; + private readonly string description; + + public ResourceProperty(string name, bool isRequired = false, string type = null, string[] allowedValues = null, string description = null) + { + this.name = name; + this.isRequired = isRequired; + this.type = type; + this.allowedValues = allowedValues; + this.description = description; + } + + public string Name + { + get { return name; } + } + + public bool IsRequired + { + get { return isRequired; } + } + + public string Type + { + get { return type; } + } + + public string[] AllowedValues + { + get { return allowedValues; } + } + + public string Description + { + get { return description; } + } + } +} diff --git a/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/Schema.cs b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/Schema.cs new file mode 100644 index 0000000000..be3e1d0600 --- /dev/null +++ b/AutoRest/Generators/AzureResourceSchema/AzureResourceSchema/Schema.cs @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.Rest.Generator.ClientModel; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Rest.Generator.AzureResourceSchema +{ + public class Schema + { + private const string resourceMethodPrefix = "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/"; + + private readonly string id; + private readonly string title; + private readonly string description; + private readonly IEnumerable resources; + + private Schema(string id, string title, string description, IEnumerable resources) + { + this.id = id; + this.title = title; + this.description = description; + this.resources = resources; + } + + public string Id + { + get { return id; } + } + + public string SchemaUri + { + get { return "http://json-schema.org/draft-04/schema#"; } + } + + public string Title + { + get { return title; } + } + + public string Description + { + get { return description; } + } + + public IEnumerable Resources + { + get { return resources; } + } + + public static Schema Parse(ServiceClient serviceClient) + { + string resourceProvider = GetResourceProvider(serviceClient); + string apiVersion = serviceClient.ApiVersion; + + string id = String.Format("http://schema.management.azure.com/schemas/{0}/{1}.json#", apiVersion, resourceProvider); + + string title = resourceProvider; + + string description = resourceProvider.Replace('.', ' ') + " Resource Types"; + + List resources = new List(); + foreach (Method resourceMethod in GetResourceMethods(serviceClient)) + { + // Azure "create resource" methods are always PUTs. + if (resourceMethod.HttpMethod == HttpMethod.Put) + { + string resourceName = GetResourceName(resourceMethod); + string resourceType = GetResourceType(resourceMethod); + string[] apiVersions = new string[] { apiVersion }; + List resourceProperties = new List(); + string resourceDescription = resourceType; + + CompositeType body = resourceMethod.Body.Type as CompositeType; + if (body != null) + { + CompositeType bodyProperties = body.Properties.Where(p => p.Name == "properties").Single().Type as CompositeType; + if (bodyProperties != null) + { + foreach (Property property in bodyProperties.Properties) + { + string propertyName = property.Name; + bool propertyIsRequired = property.IsRequired; + string propertyType = null; + string[] allowedValues = null; + string propertyDescription = String.Format("{0}: {1}", resourceType, property.Documentation); + + if(property.Type is EnumType) + { + propertyType = "string"; + + EnumType propertyEnumType = property.Type as EnumType; + allowedValues = new string[propertyEnumType.Values.Count]; + for (int i = 0; i < propertyEnumType.Values.Count; ++i) + { + allowedValues[i] = propertyEnumType.Values[i].Name; + } + } + + resourceProperties.Add(new ResourceProperty(propertyName, propertyIsRequired, propertyType, allowedValues, propertyDescription)); + } + } + } + + resources.Add(new Resource(resourceName, resourceType, apiVersions, resourceProperties, resourceDescription)); + } + } + + return new Schema(id, title, description, resources); + } + + private static IEnumerable GetResourceMethods(ServiceClient serviceClient) + { + return serviceClient.Methods.Where(method => method.Url.StartsWith(resourceMethodPrefix)); + } + + private static string GetResourceProvider(ServiceClient serviceClient) + { + return GetResourceMethods(serviceClient).Select(GetResourceProvider).Distinct().Single(); + } + + private static string GetResourceProvider(Method resourceMethod) + { + string afterPrefix = resourceMethod.Url.Substring(resourceMethodPrefix.Length); + int firstForwardSlashAfterPrefix = afterPrefix.IndexOf('/'); + return afterPrefix.Substring(0, firstForwardSlashAfterPrefix); + } + + private static string GetResourceName(Method resourceMethod) + { + string afterPrefix = resourceMethod.Url.Substring(resourceMethodPrefix.Length); + int forwardSlashIndexAfterProvider = afterPrefix.IndexOf('/'); + int resourceNameStartIndex = forwardSlashIndexAfterProvider + 1; + int forwardSlashIndexAfterResourceName = afterPrefix.IndexOf('/', resourceNameStartIndex); + + string result; + if(forwardSlashIndexAfterResourceName == -1) + { + result = afterPrefix.Substring(resourceNameStartIndex); + } + else + { + result = afterPrefix.Substring(resourceNameStartIndex, forwardSlashIndexAfterResourceName - resourceNameStartIndex); + } + + return result; + } + + private static string GetResourceType(Method resourceMethod) + { + string afterPrefix = resourceMethod.Url.Substring(resourceMethodPrefix.Length); + int forwardSlashIndexAfterProvider = afterPrefix.IndexOf('/'); + int forwardSlashIndexAfterResourceName = afterPrefix.IndexOf('/', forwardSlashIndexAfterProvider + 1); + + string result; + if(forwardSlashIndexAfterResourceName == -1) + { + result = afterPrefix; + } + else + { + result = afterPrefix.Substring(0, forwardSlashIndexAfterResourceName); + } + + return result; + } + } +}