/// <summary> /// Add a child resource schema to this JsonSchema. /// </summary> /// <param name="childResourceSchema">The child resource schema to add to this JsonSchema.</param> /// <returns></returns> public JsonSchema AddResource(JsonSchema childResourceSchema) { if (childResourceSchema == null) { throw new ArgumentNullException("childResourceSchema"); } if (resources == null) { resources = new List<JsonSchema>(); } resources.Add(childResourceSchema); return this; }
/// <summary> /// Add a new property to this JsonSchema, and then return this JsonSchema so that /// additional changes can be chained together. /// </summary> /// <param name="propertyName">The name of the property to add.</param> /// <param name="propertyDefinition">The JsonSchema definition of the property to add.</param> /// <returns></returns> public JsonSchema AddProperty(string propertyName, JsonSchema propertyDefinition) { return AddProperty(propertyName, propertyDefinition, false); }
/// <summary> /// Add a new property to this JsonSchema, and then return this JsonSchema so that /// additional changes can be chained together. /// </summary> /// <param name="propertyName">The name of the property to add.</param> /// <param name="propertyDefinition">The JsonSchema definition of the property to add.</param> /// <param name="isRequired">Whether this property is required or not.</param> /// <returns></returns> public JsonSchema AddProperty(string propertyName, JsonSchema propertyDefinition, bool isRequired) { if (string.IsNullOrWhiteSpace(propertyName)) { throw new ArgumentException("propertyName cannot be null or whitespace", "propertyName"); } if (propertyDefinition == null) { throw new ArgumentNullException("propertyDefinition"); } if (properties == null) { properties = new Dictionary<string, JsonSchema>(); } if (properties.ContainsKey(propertyName)) { throw new ArgumentException("A property with the name \"" + propertyName + "\" already exists in this JSONSchema", "propertyName"); } properties[propertyName] = propertyDefinition; if (isRequired) { AddRequired(propertyName); } return this; }
private static JsonSchema ParseEnumType(EnumType enumType) { JsonSchema result = new JsonSchema(); result.JsonType = "string"; foreach (EnumValue enumValue in enumType.Values) { result.AddEnum(enumValue.Name); } return result; }
private static JsonSchema ParsePrimaryType(PrimaryType primaryType) { JsonSchema result = new JsonSchema(); switch (primaryType.Type) { case KnownPrimaryType.Boolean: result.JsonType = "boolean"; break; case KnownPrimaryType.Int: case KnownPrimaryType.Long: result.JsonType = "integer"; break; case KnownPrimaryType.Double: result.JsonType = "number"; break; case KnownPrimaryType.Object: result.JsonType = "object"; break; case KnownPrimaryType.DateTime: case KnownPrimaryType.String: result.JsonType = "string"; break; default: Debug.Assert(false, "Unrecognized known property type: " + primaryType.Type); break; } 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); }
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; }
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()); }
private static JsonSchema ParseProperty(Property property, IDictionary<string, JsonSchema> definitionMap) { JsonSchema propertyDefinition = null; if (!property.IsReadOnly) { propertyDefinition = new JsonSchema(); IType propertyType = property.Type; CompositeType compositeType = propertyType as CompositeType; if (compositeType != null) { propertyDefinition = ParseCompositeType(compositeType, definitionMap); propertyDefinition.Description = property.Documentation; } else { DictionaryType dictionaryType = propertyType as DictionaryType; if (dictionaryType != null) { propertyDefinition.JsonType = "object"; propertyDefinition.Description = property.Documentation; PrimaryType dictionaryPrimaryType = dictionaryType.ValueType as PrimaryType; if (dictionaryPrimaryType != null) { propertyDefinition.AdditionalProperties = ParsePrimaryType(dictionaryPrimaryType); } else { CompositeType dictionaryCompositeType = dictionaryType.ValueType as CompositeType; if (dictionaryCompositeType != null) { propertyDefinition.AdditionalProperties = ParseCompositeType(dictionaryCompositeType, definitionMap); } else { Debug.Assert(false, "Unrecognized DictionaryType.ValueType: " + dictionaryType.ValueType.GetType()); } } } else { EnumType enumType = propertyType as EnumType; if (enumType != null) { propertyDefinition = ParseEnumType(enumType); propertyDefinition.Description = property.Documentation; } else { PrimaryType primaryType = propertyType as PrimaryType; if (primaryType != null) { propertyDefinition = ParsePrimaryType(primaryType); propertyDefinition.Description = property.Documentation; propertyDefinition.Format = primaryType.Format; if (property.DefaultValue != null) { propertyDefinition.AddEnum(property.DefaultValue); } } else { SequenceType sequenceType = propertyType as SequenceType; if (sequenceType != null) { propertyDefinition.JsonType = "array"; propertyDefinition.Description = property.Documentation; IType sequenceElementType = sequenceType.ElementType; CompositeType sequenceCompositeType = sequenceElementType as CompositeType; if (sequenceCompositeType != null) { propertyDefinition.Items = ParseCompositeType(sequenceCompositeType, definitionMap); } else { PrimaryType sequencePrimaryType = sequenceElementType as PrimaryType; if (sequencePrimaryType != null) { propertyDefinition.Items = ParsePrimaryType(sequencePrimaryType); } else { EnumType sequenceEnumType = sequenceElementType as EnumType; if (sequenceEnumType != null) { propertyDefinition = ParseEnumType(sequenceEnumType); } else { Debug.Assert(false, "Unrecognized SequenceType.ElementType: " + sequenceType.ElementType.GetType()); } } } } else { Debug.Assert(false, "Unrecognized property type: " + propertyType.GetType()); } } } } } } return propertyDefinition; }
public void WriteDefinitionWithTypeAndEnum() { StringWriter stringWriter = new StringWriter(); JsonTextWriter writer = new JsonTextWriter(stringWriter); writer.QuoteChar = '\''; const string definitionName = "mockDefinition"; JsonSchema definition = new JsonSchema() { JsonType = "MockType" } .AddEnum("MockEnum1", "MockEnum2"); ResourceSchemaWriter.WriteDefinition(writer, definitionName, definition); Assert.Equal("'mockDefinition':{'type':'MockType','enum':['MockEnum1','MockEnum2']}", stringWriter.ToString()); }
public void WriteDefinitionWithEnumAndUnrequiredProperty() { StringWriter stringWriter = new StringWriter(); JsonTextWriter writer = new JsonTextWriter(stringWriter); writer.QuoteChar = '\''; const string definitionName = "mockDefinition"; JsonSchema definition = new JsonSchema() .AddEnum("MockEnum1", "MockEnum2") .AddProperty("mockPropertyName", new JsonSchema()); ResourceSchemaWriter.WriteDefinition(writer, definitionName, definition); Assert.Equal("'mockDefinition':{'enum':['MockEnum1','MockEnum2'],'properties':{'mockPropertyName':{'oneOf':[{},{'$ref':'http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression'}]}}}", stringWriter.ToString()); }
public void WriteDefinitionWithType() { StringWriter stringWriter = new StringWriter(); JsonTextWriter writer = new JsonTextWriter(stringWriter); writer.QuoteChar = '\''; const string definitionName = "mockDefinition"; JsonSchema definition = new JsonSchema(); definition.JsonType = "MockType"; ResourceSchemaWriter.WriteDefinition(writer, definitionName, definition); Assert.Equal("'mockDefinition':{'type':'MockType'}", stringWriter.ToString()); }
private static void WriteDefinition(JsonWriter writer, JsonSchema definition) { if (definition == null) { throw new ArgumentNullException("definition"); } writer.WriteStartObject(); WriteProperty(writer, "type", definition.JsonType); WriteStringArray(writer, "enum", definition.Enum); WriteProperty(writer, "format", definition.Format); WriteProperty(writer, "$ref", definition.Ref); WriteDefinition(writer, "items", definition.Items); WriteDefinition(writer, "additionalProperties", definition.AdditionalProperties); WriteDefinitionMap(writer, "properties", definition.Properties, addExpressionReferences: true); WriteDefinitionArray(writer, "resources", definition.Resources); WriteStringArray(writer, "required", definition.Required); WriteProperty(writer, "description", definition.Description); writer.WriteEndObject(); }
public static void WriteDefinition(JsonWriter writer, string resourceName, JsonSchema definition) { if (writer == null) { throw new ArgumentNullException("writer"); } if (definition != null) { writer.WritePropertyName(resourceName); WriteDefinition(writer, definition); } }
/// <summary> /// Create a new JsonSchema that is an exact copy of this one. /// </summary> /// <returns></returns> public JsonSchema Clone() { JsonSchema result = new JsonSchema(); result.Ref = Ref; result.Items = Clone(Items); result.Description = Description; result.JsonType = JsonType; result.AdditionalProperties = Clone(AdditionalProperties); result.enumList = Clone(Enum); result.properties = Clone(Properties); result.requiredList = Clone(Required); result.resources = Clone(Resources); 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; }
private static JsonSchema Clone(JsonSchema toClone) { JsonSchema result = null; if (toClone != null) { result = toClone.Clone(); } return result; }
private static JsonSchema ParseProperty(Property property, IDictionary <string, JsonSchema> definitionMap) { JsonSchema propertyDefinition = null; if (!property.IsReadOnly) { propertyDefinition = new JsonSchema(); IType propertyType = property.Type; CompositeType compositeType = propertyType as CompositeType; if (compositeType != null) { propertyDefinition = ParseCompositeType(compositeType, definitionMap); propertyDefinition.Description = property.Documentation; } else { DictionaryType dictionaryType = propertyType as DictionaryType; if (dictionaryType != null) { propertyDefinition.JsonType = "object"; propertyDefinition.Description = property.Documentation; PrimaryType dictionaryPrimaryType = dictionaryType.ValueType as PrimaryType; if (dictionaryPrimaryType != null) { propertyDefinition.AdditionalProperties = ParsePrimaryType(dictionaryPrimaryType); } else { CompositeType dictionaryCompositeType = dictionaryType.ValueType as CompositeType; if (dictionaryCompositeType != null) { propertyDefinition.AdditionalProperties = ParseCompositeType(dictionaryCompositeType, definitionMap); } else { Debug.Assert(false, "Unrecognized DictionaryType.ValueType: " + dictionaryType.ValueType.GetType()); } } } else { EnumType enumType = propertyType as EnumType; if (enumType != null) { propertyDefinition = ParseEnumType(enumType); propertyDefinition.Description = property.Documentation; } else { PrimaryType primaryType = propertyType as PrimaryType; if (primaryType != null) { propertyDefinition = ParsePrimaryType(primaryType); propertyDefinition.Description = property.Documentation; propertyDefinition.Format = primaryType.Format; if (property.DefaultValue != null) { propertyDefinition.AddEnum(property.DefaultValue); } } else { SequenceType sequenceType = propertyType as SequenceType; if (sequenceType != null) { propertyDefinition.JsonType = "array"; propertyDefinition.Description = property.Documentation; IType sequenceElementType = sequenceType.ElementType; CompositeType sequenceCompositeType = sequenceElementType as CompositeType; if (sequenceCompositeType != null) { propertyDefinition.Items = ParseCompositeType(sequenceCompositeType, definitionMap); } else { PrimaryType sequencePrimaryType = sequenceElementType as PrimaryType; if (sequencePrimaryType != null) { propertyDefinition.Items = ParsePrimaryType(sequencePrimaryType); } else { EnumType sequenceEnumType = sequenceElementType as EnumType; if (sequenceEnumType != null) { propertyDefinition = ParseEnumType(sequenceEnumType); } else { Debug.Assert(false, "Unrecognized SequenceType.ElementType: " + sequenceType.ElementType.GetType()); } } } } else { Debug.Assert(false, "Unrecognized property type: " + propertyType.GetType()); } } } } } } return(propertyDefinition); }