private static JsonSchema ParseCompositeType(CompositeType compositeType, IDictionary <string, JsonSchema> definitionMap) { JsonSchema result = new JsonSchema(); string definitionName = compositeType.Name; if (!definitionMap.ContainsKey(definitionName)) { JsonSchema definition = new JsonSchema(); definitionMap.Add(definitionName, definition); definition.JsonType = "object"; foreach (Property subProperty in compositeType.ComposedProperties) { JsonSchema subPropertyDefinition = ParseProperty(subProperty, definitionMap); if (subPropertyDefinition != null) { definition.AddProperty(subProperty.Name, subPropertyDefinition, subProperty.IsRequired); } } definition.Description = compositeType.Documentation; } result.Ref = "#/definitions/" + definitionName; return(result); }
private static JsonSchema ParseCompositeType(Property property, CompositeType compositeType, IDictionary <string, JsonSchema> definitions, IEnumerable <CompositeType> modelTypes) { string definitionName = compositeType.Name; if (!definitions.ContainsKey(definitionName)) { JsonSchema definition = new JsonSchema() { JsonType = "object", Description = compositeType.Documentation }; // This definition must be added to the definition map before we start parsing // its properties because its properties may recursively reference back to this // definition. definitions.Add(definitionName, definition); foreach (Property subProperty in compositeType.ComposedProperties) { JsonSchema subPropertyDefinition = ParseType(subProperty, subProperty.Type, definitions, modelTypes); if (subPropertyDefinition != null) { definition.AddProperty(subProperty.Name, subPropertyDefinition, subProperty.IsRequired); } } string discriminatorPropertyName = compositeType.PolymorphicDiscriminator; if (!string.IsNullOrWhiteSpace(discriminatorPropertyName)) { CompositeType[] subTypes = modelTypes.Where(modelType => modelType.BaseModelType == compositeType).ToArray(); if (subTypes != null && subTypes.Length > 0) { JsonSchema discriminatorDefinition = new JsonSchema() { JsonType = "string" }; if (subTypes.Length == 1) { CompositeType subType = subTypes[0]; if (subType != null) { foreach (Property subTypeProperty in subType.Properties) { JsonSchema subTypePropertyDefinition = ParseType(subTypeProperty, subTypeProperty.Type, definitions, modelTypes); if (subTypePropertyDefinition != null) { definition.AddProperty(subTypeProperty.Name, subTypePropertyDefinition, subTypeProperty.IsRequired); } } const string discriminatorValueExtensionName = "x-ms-discriminator-value"; if (subType.ComposedExtensions.ContainsKey(discriminatorValueExtensionName)) { string discriminatorValue = subType.ComposedExtensions[discriminatorValueExtensionName] as string; if (!string.IsNullOrWhiteSpace(discriminatorValue)) { discriminatorDefinition.AddEnum(discriminatorValue); } } } definition.AddProperty(discriminatorPropertyName, discriminatorDefinition); } else { string errorMessage = string.Format( CultureInfo.CurrentCulture, "Multiple sub-types ({0}) of a polymorphic discriminated type ({1}) are not currently supported.", string.Join(", ", subTypes.Select(subType => subType.Name)), compositeType.Name); throw new NotSupportedException(errorMessage); } } } } JsonSchema result = new JsonSchema() { Ref = "#/definitions/" + definitionName }; if (property != null) { result.Description = RemovePossibleValuesFromDescription(property.Documentation); } return(result); }
/// <summary> /// Parse a ResourceSchemaModel from the provided ServiceClient. /// </summary> /// <param name="serviceClient"></param> /// <returns></returns> public static IDictionary <string, ResourceSchema> Parse(ServiceClient serviceClient) { if (serviceClient == null) { throw new ArgumentNullException(nameof(serviceClient)); } string apiVersion = serviceClient.ApiVersion; if (string.IsNullOrWhiteSpace(apiVersion)) { throw new ArgumentException("No API version is provided in the swagger document."); } Dictionary <string, ResourceSchema> resourceSchemas = new Dictionary <string, ResourceSchema>(); foreach (Method method in serviceClient.Methods) { if (method.HttpMethod != HttpMethod.Put || method.ReturnType.Body == null || !(method.ReturnType.Body is CompositeType) || string.IsNullOrWhiteSpace(method.Url) || !method.Url.StartsWith(resourceMethodPrefix, StringComparison.OrdinalIgnoreCase) || !method.Url.EndsWith("}", StringComparison.OrdinalIgnoreCase)) { continue; } string afterPrefix = method.Url.Substring(resourceMethodPrefix.Length); int forwardSlashIndexAfterProvider = afterPrefix.IndexOf('/'); string resourceProvider = afterPrefix.Substring(0, forwardSlashIndexAfterProvider); if (IsPathVariable(resourceProvider)) { // If the resourceProvider is a path variable, such as {someValue}, then this // is not a create resource method. Skip it. continue; } ResourceSchema resourceSchema; if (!resourceSchemas.ContainsKey(resourceProvider)) { resourceSchema = new ResourceSchema(); resourceSchema.Id = string.Format(CultureInfo.InvariantCulture, "http://schema.management.azure.com/schemas/{0}/{1}.json#", apiVersion, resourceProvider); resourceSchema.Title = resourceProvider; resourceSchema.Description = resourceProvider.Replace('.', ' ') + " Resource Types"; resourceSchema.Schema = "http://json-schema.org/draft-04/schema#"; resourceSchemas.Add(resourceProvider, resourceSchema); } else { resourceSchema = resourceSchemas[resourceProvider]; } string methodUrlPathAfterProvider = afterPrefix.Substring(forwardSlashIndexAfterProvider + 1); string[] resourceTypes = ParseResourceTypes(resourceProvider, methodUrlPathAfterProvider, method); foreach (string resourceType in resourceTypes) { JsonSchema resourceDefinition = new JsonSchema(); resourceDefinition.JsonType = "object"; resourceDefinition.AddProperty("type", JsonSchema.CreateStringEnum(resourceType), true); resourceDefinition.AddProperty("apiVersion", JsonSchema.CreateStringEnum(apiVersion), true); if (method.Body != null) { CompositeType body = method.Body.Type as CompositeType; Debug.Assert(body != null, "The create resource method's body must be a CompositeType and cannot be null."); if (body != null) { foreach (Property property in body.Properties) { if (!resourceDefinition.Properties.Keys.Contains(property.Name)) { JsonSchema propertyDefinition = ParseType(property, property.Type, resourceSchema.Definitions, serviceClient.ModelTypes); if (propertyDefinition != null) { resourceDefinition.AddProperty(property.Name, propertyDefinition, property.IsRequired || property.Name == "properties"); } } } } } resourceDefinition.Description = resourceType; string resourcePropertyName = resourceType.Substring(resourceProvider.Length + 1).Replace('/', '_'); Debug.Assert(!resourceSchema.ResourceDefinitions.ContainsKey(resourcePropertyName)); resourceSchema.AddResourceDefinition(resourcePropertyName, resourceDefinition); } } // This loop adds child resource schemas to their parent resource schemas. We can't do // this until we're done adding all resources as top level resources, though, because // it's possible that we will parse a child resource before we parse the parent // resource. foreach (ResourceSchema resourceSchema in resourceSchemas.Values) { // By iterating over the reverse order of the defined resource definitions, I'm // counting on the resource definitions being in sorted order. That way I'm // guaranteed to visit child resource definitions before I visit their parent // resource definitions. By doing this, I've guaranteed that grandchildren resource // definitions will be added to their grandparent (and beyond) ancestor // resource definitions. foreach (string resourcePropertyName in resourceSchema.ResourceDefinitions.Keys.Reverse()) { JsonSchema resourceDefinition = resourceSchema.ResourceDefinitions[resourcePropertyName]; string resourceType = resourceDefinition.ResourceType; int lastSlashIndex = resourceType.LastIndexOf('/'); string parentResourceType = resourceType.Substring(0, lastSlashIndex); JsonSchema parentResourceDefinition = resourceSchema.GetResourceDefinitionByResourceType(parentResourceType); if (parentResourceDefinition != null) { string childResourceType = resourceType.Substring(lastSlashIndex + 1); JsonSchema childResourceDefinition = resourceDefinition.Clone(); childResourceDefinition.ResourceType = childResourceType; string childResourceDefinitionPropertyName = string.Join("_", resourcePropertyName, "childResource"); resourceSchema.AddDefinition(childResourceDefinitionPropertyName, childResourceDefinition); JsonSchema childResources; if (parentResourceDefinition.Properties.ContainsKey("resources")) { childResources = parentResourceDefinition.Properties["resources"]; } else { childResources = new JsonSchema() { JsonType = "array", Items = new JsonSchema() }; parentResourceDefinition.AddProperty("resources", childResources); } childResources.Items.AddOneOf(new JsonSchema() { Ref = "#/definitions/" + childResourceDefinitionPropertyName, }); } } } return(resourceSchemas); }
private static JsonSchema ParseCompositeType(CompositeType compositeType, IDictionary<string, JsonSchema> definitionMap) { JsonSchema result = new JsonSchema(); string definitionName = compositeType.Name; if (!definitionMap.ContainsKey(definitionName)) { JsonSchema definition = new JsonSchema(); definitionMap.Add(definitionName, definition); definition.JsonType = "object"; foreach (Property subProperty in compositeType.ComposedProperties) { JsonSchema subPropertyDefinition = ParseProperty(subProperty, definitionMap); if (subPropertyDefinition != null) { definition.AddProperty(subProperty.Name, subPropertyDefinition, subProperty.IsRequired); } } definition.Description = compositeType.Documentation; } result.Ref = "#/definitions/" + definitionName; return result; }
/// <summary> /// Parse a ResourceSchemaModel from the provided ServiceClient. /// </summary> /// <param name="serviceClient"></param> /// <returns></returns> public static IDictionary<string, ResourceSchema> Parse(ServiceClient serviceClient) { if (serviceClient == null) { throw new ArgumentNullException("serviceClient"); } IDictionary<string, ResourceSchema> result = new Dictionary<string, ResourceSchema>(); List<Method> createResourceMethods = new List<Method>(); foreach (Method method in serviceClient.Methods) { if (IsCreateResourceMethod(method)) { createResourceMethods.Add(method); } } string apiVersion = serviceClient.ApiVersion; foreach (Method createResourceMethod in createResourceMethods) { string afterPrefix = createResourceMethod.Url.Substring(resourceMethodPrefix.Length); int forwardSlashIndexAfterProvider = afterPrefix.IndexOf('/'); string resourceProvider = afterPrefix.Substring(0, forwardSlashIndexAfterProvider); ResourceSchema resourceSchema; if (!result.ContainsKey(resourceProvider)) { resourceSchema = new ResourceSchema(); if (apiVersion != null) { resourceSchema.Id = string.Format(CultureInfo.InvariantCulture, "http://schema.management.azure.com/schemas/{0}/{1}.json#", apiVersion, resourceProvider); } resourceSchema.Title = resourceProvider; resourceSchema.Description = resourceProvider.Replace('.', ' ') + " Resource Types"; resourceSchema.Schema = "http://json-schema.org/draft-04/schema#"; result.Add(resourceProvider, resourceSchema); } else { resourceSchema = result[resourceProvider]; } string methodUrlPathAfterProvider = afterPrefix.Substring(forwardSlashIndexAfterProvider + 1); string[] resourceTypes = GetResourceTypes(resourceProvider, methodUrlPathAfterProvider, createResourceMethod.Parameters); foreach (string resourceType in resourceTypes) { JsonSchema resourceDefinition = new JsonSchema(); resourceDefinition.JsonType = "object"; resourceDefinition.AddProperty("type", new JsonSchema() { JsonType = "string" }.AddEnum(resourceType), true); if (!string.IsNullOrWhiteSpace(apiVersion)) { resourceDefinition.AddProperty("apiVersion", new JsonSchema() { JsonType = "string" }.AddEnum(apiVersion), true); } if (createResourceMethod.Body != null) { CompositeType body = createResourceMethod.Body.Type as CompositeType; Debug.Assert(body != null, "The create resource method's body must be a CompositeType and cannot be null."); if (body != null) { foreach (Property property in body.Properties) { if (!resourceDefinition.Properties.Keys.Contains(property.Name)) { JsonSchema propertyDefinition = ParseProperty(property, resourceSchema.Definitions); if (propertyDefinition != null) { resourceDefinition.AddProperty(property.Name, propertyDefinition, property.IsRequired || property.Name == "properties"); } } } } } resourceDefinition.Description = resourceType; string resourcePropertyName = resourceType.Substring(resourceProvider.Length + 1).Replace('/', '_'); Debug.Assert(!resourceSchema.ResourceDefinitions.ContainsKey(resourcePropertyName)); resourceSchema.AddResourceDefinition(resourcePropertyName, resourceDefinition); } } // This loop adds child resource schemas to their parent resource schemas. We can't do // this until we're done adding all resources as top level resources, though, because // it's possible that we will parse a child resource before we parse the parent // resource. foreach (ResourceSchema resourceSchema in result.Values) { // By iterating over the reverse order of the defined resource definitions, I'm // counting on the resource definitions being in sorted order. That way I'm // guaranteed to visit child resource definitions before I visit their parent // resource definitions. By doing this, I've guaranteed that grandchildren resource // definitions will be added to their grandparent (and beyond) ancestor // resource definitions. foreach (string resourcePropertyName in resourceSchema.ResourceDefinitions.Keys.Reverse()) { JsonSchema resourceDefinition = resourceSchema.ResourceDefinitions[resourcePropertyName]; string resourceType = resourceDefinition.ResourceType; int lastSlashIndex = resourceType.LastIndexOf('/'); string parentResourceType = resourceType.Substring(0, lastSlashIndex); JsonSchema parentResourceDefinition = resourceSchema.GetResourceDefinitionByResourceType(parentResourceType); if (parentResourceDefinition != null) { string childResourceType = resourceType.Substring(lastSlashIndex + 1); JsonSchema childResourceDefinition = resourceDefinition.Clone(); childResourceDefinition.ResourceType = childResourceType; string childResourceDefinitionPropertyName = string.Join("_", resourcePropertyName, "childResource"); resourceSchema.AddDefinition(childResourceDefinitionPropertyName, childResourceDefinition); parentResourceDefinition.AddResource(new JsonSchema() { Ref = "#/definitions/" + childResourceDefinitionPropertyName, }); } } } return result; }
public void WriteDefinitionWithRequiredPropertyAndDescription() { StringWriter stringWriter = new StringWriter(); JsonTextWriter writer = new JsonTextWriter(stringWriter); writer.QuoteChar = '\''; const string definitionName = "mockDefinition"; JsonSchema definition = new JsonSchema(); definition.AddProperty("mockPropertyName", new JsonSchema(), true); definition.Description = "MockDescription"; ResourceSchemaWriter.WriteDefinition(writer, definitionName, definition); Assert.Equal("'mockDefinition':{'properties':{'mockPropertyName':{'oneOf':[{},{'$ref':'http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression'}]}},'required':['mockPropertyName'],'description':'MockDescription'}", stringWriter.ToString()); }
/// <summary> /// Parse a ResourceSchemaModel from the provided ServiceClient. /// </summary> /// <param name="serviceClient"></param> /// <returns></returns> public static IDictionary <string, ResourceSchema> Parse(ServiceClient serviceClient) { if (serviceClient == null) { throw new ArgumentNullException("serviceClient"); } IDictionary <string, ResourceSchema> result = new Dictionary <string, ResourceSchema>(); List <Method> createResourceMethods = new List <Method>(); foreach (Method method in serviceClient.Methods) { if (IsCreateResourceMethod(method)) { createResourceMethods.Add(method); } } string apiVersion = serviceClient.ApiVersion; foreach (Method createResourceMethod in createResourceMethods) { string afterPrefix = createResourceMethod.Url.Substring(resourceMethodPrefix.Length); int forwardSlashIndexAfterProvider = afterPrefix.IndexOf('/'); string resourceProvider = afterPrefix.Substring(0, forwardSlashIndexAfterProvider); ResourceSchema resourceSchema; if (!result.ContainsKey(resourceProvider)) { resourceSchema = new ResourceSchema(); if (apiVersion != null) { resourceSchema.Id = string.Format(CultureInfo.InvariantCulture, "http://schema.management.azure.com/schemas/{0}/{1}.json#", apiVersion, resourceProvider); } resourceSchema.Title = resourceProvider; resourceSchema.Description = resourceProvider.Replace('.', ' ') + " Resource Types"; resourceSchema.Schema = "http://json-schema.org/draft-04/schema#"; result.Add(resourceProvider, resourceSchema); } else { resourceSchema = result[resourceProvider]; } string methodUrlPathAfterProvider = afterPrefix.Substring(forwardSlashIndexAfterProvider + 1); string[] resourceTypes = GetResourceTypes(resourceProvider, methodUrlPathAfterProvider, createResourceMethod.Parameters); foreach (string resourceType in resourceTypes) { JsonSchema resourceDefinition = new JsonSchema(); resourceDefinition.JsonType = "object"; resourceDefinition.AddProperty("type", new JsonSchema() { JsonType = "string" }.AddEnum(resourceType), true); if (!string.IsNullOrWhiteSpace(apiVersion)) { resourceDefinition.AddProperty("apiVersion", new JsonSchema() { JsonType = "string" }.AddEnum(apiVersion), true); } if (createResourceMethod.Body != null) { CompositeType body = createResourceMethod.Body.Type as CompositeType; Debug.Assert(body != null, "The create resource method's body must be a CompositeType and cannot be null."); if (body != null) { foreach (Property property in body.Properties) { if (!resourceDefinition.Properties.Keys.Contains(property.Name)) { JsonSchema propertyDefinition = ParseProperty(property, resourceSchema.Definitions); if (propertyDefinition != null) { resourceDefinition.AddProperty(property.Name, propertyDefinition, property.IsRequired || property.Name == "properties"); } } } } } resourceDefinition.Description = resourceType; string resourcePropertyName = resourceType.Substring(resourceProvider.Length + 1).Replace('/', '_'); Debug.Assert(!resourceSchema.ResourceDefinitions.ContainsKey(resourcePropertyName)); resourceSchema.AddResourceDefinition(resourcePropertyName, resourceDefinition); } } // This loop adds child resource schemas to their parent resource schemas. We can't do // this until we're done adding all resources as top level resources, though, because // it's possible that we will parse a child resource before we parse the parent // resource. foreach (ResourceSchema resourceSchema in result.Values) { // By iterating over the reverse order of the defined resource definitions, I'm // counting on the resource definitions being in sorted order. That way I'm // guaranteed to visit child resource definitions before I visit their parent // resource definitions. By doing this, I've guaranteed that grandchildren resource // definitions will be added to their grandparent (and beyond) ancestor // resource definitions. foreach (string resourcePropertyName in resourceSchema.ResourceDefinitions.Keys.Reverse()) { JsonSchema resourceDefinition = resourceSchema.ResourceDefinitions[resourcePropertyName]; string resourceType = resourceDefinition.ResourceType; int lastSlashIndex = resourceType.LastIndexOf('/'); string parentResourceType = resourceType.Substring(0, lastSlashIndex); JsonSchema parentResourceDefinition = resourceSchema.GetResourceDefinitionByResourceType(parentResourceType); if (parentResourceDefinition != null) { string childResourceType = resourceType.Substring(lastSlashIndex + 1); JsonSchema childResourceDefinition = resourceDefinition.Clone(); childResourceDefinition.ResourceType = childResourceType; string childResourceDefinitionPropertyName = string.Join("_", resourcePropertyName, "childResource"); resourceSchema.AddDefinition(childResourceDefinitionPropertyName, childResourceDefinition); parentResourceDefinition.AddResource(new JsonSchema() { Ref = "#/definitions/" + childResourceDefinitionPropertyName, }); } } } return(result); }