public static void Write(TextWriter writer, ResourceSchema resourceSchema) { if (writer == null) { throw new ArgumentNullException("writer"); } if (resourceSchema == null) { throw new ArgumentNullException("resourceSchema"); } var mdWriter = new ResourceMarkdownWriter(writer, resourceSchema); mdWriter.Generate(); }
public static void Write(TextWriter writer, ResourceSchema resourceSchema) { if (writer == null) { throw new ArgumentNullException("writer"); } if (resourceSchema == null) { throw new ArgumentNullException("resourceSchema"); } using (JsonTextWriter jsonWriter = new JsonTextWriter(writer)) { jsonWriter.Formatting = Formatting.Indented; jsonWriter.Indentation = 2; jsonWriter.IndentChar = ' '; jsonWriter.QuoteChar = '\"'; Write(jsonWriter, resourceSchema); } }
public static void Write(JsonWriter writer, ResourceSchema resourceSchema) { if (writer == null) { throw new ArgumentNullException("writer"); } if (resourceSchema == null) { throw new ArgumentNullException("resourceSchema"); } writer.WriteStartObject(); WriteProperty(writer, "id", resourceSchema.Id); WriteProperty(writer, "$schema", resourceSchema.Schema); WriteProperty(writer, "title", resourceSchema.Title); WriteProperty(writer, "description", resourceSchema.Description); WriteDefinitionMap(writer, "resourceDefinitions", resourceSchema.ResourceDefinitions, sortDefinitions: true); WriteDefinitionMap(writer, "definitions", resourceSchema.Definitions, sortDefinitions: true); writer.WriteEndObject(); }
public static Markdown[] Generate(ResourceSchema resourceSchema) { if (resourceSchema == null) { throw new ArgumentNullException("resourceSchema"); } var mds = new Markdown[resourceSchema.ResourceDefinitions.Keys.Count]; int index = 0; foreach (var resDefName in resourceSchema.ResourceDefinitions.Keys) { var resDef = resourceSchema.ResourceDefinitions[resDefName]; var writer = new StringWriter(); var mdWriter = new ResourceMarkdownGenerator(writer, resourceSchema); mdWriter.Generate(resDef); mds[index] = new Markdown(resDefName, writer.ToString()); ++index; } return(mds); }
public static void Write(JsonWriter writer, ResourceSchema resourceSchema) { if (writer == null) { throw new ArgumentNullException("writer"); } if (resourceSchema == null) { throw new ArgumentNullException("resourceSchema"); } writer.WriteStartObject(); WriteProperty(writer, "id", resourceSchema.Id); WriteProperty(writer, "$schema", resourceSchema.Schema); WriteProperty(writer, "title", resourceSchema.Title); WriteProperty(writer, "description", resourceSchema.Description); var rgDefinitions = GetResourceDefinitions(resourceSchema, ScopeType.ResourceGroup); WriteDefinitionMap(writer, "resourceDefinitions", rgDefinitions, sortDefinitions: true, addExpressionReferences: false); var subDefinitions = GetResourceDefinitions(resourceSchema, ScopeType.Subcription); if (subDefinitions.Any()) { WriteDefinitionMap(writer, "subscription_resourceDefinitions", subDefinitions, sortDefinitions: true, addExpressionReferences: false); } var mgDefinitions = GetResourceDefinitions(resourceSchema, ScopeType.ManagementGroup); if (mgDefinitions.Any()) { WriteDefinitionMap(writer, "managementGroup_resourceDefinitions", mgDefinitions, sortDefinitions: true, addExpressionReferences: false); } var tenantDefinitions = GetResourceDefinitions(resourceSchema, ScopeType.Tenant); if (tenantDefinitions.Any()) { WriteDefinitionMap(writer, "tenant_resourceDefinitions", tenantDefinitions, sortDefinitions: true, addExpressionReferences: false); } var extDefinitions = GetResourceDefinitions(resourceSchema, ScopeType.Extension); if (extDefinitions.Any()) { WriteDefinitionMap(writer, "extension_resourceDefinitions", extDefinitions, sortDefinitions: true, addExpressionReferences: false); } var unknownDefinitions = GetResourceDefinitions(resourceSchema, ScopeType.Unknown); if (unknownDefinitions.Any()) { WriteDefinitionMap(writer, "unknown_resourceDefinitions", unknownDefinitions, sortDefinitions: true, addExpressionReferences: false); } WriteDefinitionMap(writer, "definitions", resourceSchema.Definitions, sortDefinitions: true, addExpressionReferences: false); writer.WriteEndObject(); }
private static IDictionary <string, JsonSchema> GetResourceDefinitions(ResourceSchema resourceSchema, ScopeType scopeType) => resourceSchema.ResourceDefinitions .Where(kvp => kvp.Key.ScopeType == scopeType) .ToDictionary(kvp => ResourceSchema.FormatResourceSchemaKey(kvp.Key.ResourceTypeSegments), kvp => kvp.Value);
private static ResourceSchema CreateSchema(ProviderDefinition providerDefinition) { var processedSchemas = new Dictionary <string, JsonSchema>(StringComparer.OrdinalIgnoreCase); var resourceDefinitions = new Dictionary <ResourceDescriptor, JsonSchema>(ResourceDescriptor.Comparer); // Order by resource type length to process parent resources before child resources var definitionsByDescriptor = providerDefinition .ResourceDefinitions.ToLookup(x => x.Descriptor, ResourceDescriptor.Comparer) .OrderBy(grouping => grouping.Key.ResourceTypeSegments.Count); foreach (var definitionGrouping in definitionsByDescriptor) { var descriptor = definitionGrouping.Key; var definitions = definitionGrouping.ToArray(); if (processedSchemas.ContainsKey(descriptor.FullyQualifiedTypeWithScope)) { LogWarning($"Found duplicate definition for type {descriptor.FullyQualifiedType} in scope {descriptor.ScopeType}"); continue; } if (definitions.Length > 1 && descriptor.HasVariableName) { var selectedDefinition = definitions.First(); foreach (var definition in definitions.Skip(1)) { LogWarning($"Found duplicate definition for variable-named type {descriptor.FullyQualifiedType}. Skipping definition with path '{definition.Descriptor.XmsMetadata.path}'."); } LogWarning($"Found duplicate definition for variable-named type {descriptor.FullyQualifiedType}. Using definition with path '{selectedDefinition.Descriptor.XmsMetadata.path}'."); definitions = new[] { selectedDefinition }; } // Add schema to global resources { JsonSchema schema; if (definitions.Length == 1) { schema = definitions.Single().BaseSchema.Clone(); schema.AddPropertyWithOverwrite("name", definitions.Single().Name.NameSchema.Clone(), true); schema.AddPropertyWithOverwrite("type", JsonSchema.CreateSingleValuedEnum(descriptor.FullyQualifiedType), true); schema.AddPropertyWithOverwrite("apiVersion", JsonSchema.CreateSingleValuedEnum(descriptor.ApiVersion), true); } else { schema = new JsonSchema { JsonType = "object", Description = descriptor.FullyQualifiedType, }; foreach (var definition in definitions) { if (!definition.Name.HasConstantName) { throw new InvalidOperationException($"Unable to reconcile variable-named resource {descriptor.FullyQualifiedType}"); } var oneOfSchema = definition.BaseSchema.Clone(); oneOfSchema.AddPropertyWithOverwrite("name", definition.Name.NameSchema.Clone(), true); schema.AddOneOf(oneOfSchema); } schema.AddPropertyWithOverwrite("type", JsonSchema.CreateSingleValuedEnum(descriptor.FullyQualifiedType), true); schema.AddPropertyWithOverwrite("apiVersion", JsonSchema.CreateSingleValuedEnum(descriptor.ApiVersion), true); } processedSchemas[descriptor.FullyQualifiedTypeWithScope] = schema; resourceDefinitions[descriptor] = schema; } // Add schema to parent resources if necessary if (!descriptor.IsRootType && processedSchemas.TryGetValue(ResourceDescriptor.FormatParentFullyQualifiedTypeWithScope(descriptor), out var parentSchema)) { if (!parentSchema.Properties.ContainsKey("resources")) { parentSchema.AddProperty("resources", new JsonSchema { JsonType = "array", Items = new JsonSchema(), }); } JsonSchema childSchema; if (definitions.Length == 1) { childSchema = definitions.Single().BaseSchema.Clone(); var resourceName = definitions.Single().Name; var nameSchema = resourceName.HasConstantName ? JsonSchema.CreateSingleValuedEnum(resourceName.NameString) : resourceName.NameSchema; nameSchema.Description = resourceName.NameSchema?.Description; childSchema.AddPropertyWithOverwrite("name", nameSchema, true); childSchema.AddPropertyWithOverwrite("type", JsonSchema.CreateSingleValuedEnum(descriptor.ResourceTypeSegments.Last()), true); childSchema.AddPropertyWithOverwrite("apiVersion", JsonSchema.CreateSingleValuedEnum(descriptor.ApiVersion), true); } else { childSchema = new JsonSchema { JsonType = "object", Description = descriptor.FullyQualifiedType, }; foreach (var definition in definitions) { if (!definition.Name.HasConstantName) { throw new InvalidOperationException($"Unable to reconcile variable-named resource {descriptor.FullyQualifiedType}"); } var oneOfSchema = definition.BaseSchema.Clone(); var nameSchema = JsonSchema.CreateSingleValuedEnum(definition.Name.NameString); nameSchema.Description = definition.Name.NameSchema?.Description; oneOfSchema.AddPropertyWithOverwrite("name", nameSchema, true); childSchema.AddOneOf(oneOfSchema); } childSchema.AddPropertyWithOverwrite("type", JsonSchema.CreateSingleValuedEnum(descriptor.ResourceTypeSegments.Last()), true); childSchema.AddPropertyWithOverwrite("apiVersion", JsonSchema.CreateSingleValuedEnum(descriptor.ApiVersion), true); } var childDefinitionName = ResourceSchema.FormatResourceSchemaKey(descriptor.ResourceTypeSegments) + "_childResource"; providerDefinition.SchemaDefinitions.Add(childDefinitionName, childSchema); parentSchema.Properties["resources"].Items.AddOneOf(new JsonSchema { Ref = "#/definitions/" + childDefinitionName, }); } } return(new ResourceSchema { Id = $"https://schema.management.azure.com/schemas/{providerDefinition.ApiVersion}/{providerDefinition.Namespace}.json#", Title = providerDefinition.Namespace, Description = providerDefinition.Namespace.Replace('.', ' ') + " Resource Types", Schema = "http://json-schema.org/draft-04/schema#", Definitions = providerDefinition.SchemaDefinitions, ResourceDefinitions = resourceDefinitions, }); }
private ResourceMarkdownGenerator(TextWriter writer, ResourceSchema resourceSchema) { _writer = new MarkdownWriter(writer); _schema = resourceSchema; }
/// <summary> /// Parse a ResourceSchemaModel from the provided ServiceClient. /// </summary> /// <param name="serviceClient"></param> /// <returns></returns> public static IDictionary <string, ResourceSchema> Parse(CodeModel serviceClient, string apiVersion, bool multiScope) { if (serviceClient == null) { throw new ArgumentNullException(nameof(serviceClient)); } var providerSchemas = new Dictionary <string, ResourceSchema>(); foreach (var method in serviceClient.Methods.Where(method => ShouldProcess(serviceClient, method, apiVersion))) { var(success, failureReason, resourceDescriptors) = ParseMethod(method, apiVersion); if (!success) { LogMessage($"Skipping path '{method.Url}': {failureReason}"); continue; } foreach (var descriptor in resourceDescriptors) { if (!multiScope && descriptor.ScopeType != ScopeType.ResourceGroup) { LogMessage($"Skipping resource type {descriptor.FullyQualifiedType} under path '{method.Url}': Detected scope type {descriptor.ScopeType} and multi-scope is disabled"); continue; } if (!providerSchemas.ContainsKey(descriptor.ProviderNamespace)) { providerSchemas.Add(descriptor.ProviderNamespace, new ResourceSchema { Id = $"https://schema.management.azure.com/schemas/{apiVersion}/{descriptor.ProviderNamespace}.json#", Title = descriptor.ProviderNamespace, Description = descriptor.ProviderNamespace.Replace('.', ' ') + " Resource Types", Schema = "http://json-schema.org/draft-04/schema#" }); } var providerSchema = providerSchemas[descriptor.ProviderNamespace]; var resourceSchema = new JsonSchema { JsonType = "object", ResourceType = descriptor.FullyQualifiedType, Description = descriptor.FullyQualifiedType, }; JsonSchema nameSchema; (success, failureReason, nameSchema) = ParseNameSchema(serviceClient, method, providerSchema, descriptor); if (!success) { LogMessage($"Skipping resource type {descriptor.FullyQualifiedType} under path '{method.Url}': {failureReason}"); continue; } resourceSchema.AddProperty("name", nameSchema, true); resourceSchema.AddProperty("type", JsonSchema.CreateSingleValuedEnum(descriptor.FullyQualifiedType), true); resourceSchema.AddProperty("apiVersion", JsonSchema.CreateSingleValuedEnum(apiVersion), true); if (method.Body?.ModelType is CompositeType body) { foreach (var property in body.ComposedProperties) { if (property.SerializedName != null && !resourceSchema.Properties.Keys.Contains(property.SerializedName)) { var propertyDefinition = ParseType(property, property.ModelType, providerSchema.Definitions, serviceClient.ModelTypes); if (propertyDefinition != null) { resourceSchema.AddProperty(property.SerializedName, propertyDefinition, property.IsRequired || property.SerializedName == "properties"); } } } HandlePolymorphicType(resourceSchema, body, providerSchema.Definitions, serviceClient.ModelTypes); } string resourcePropertyName = ResourceSchema.FormatResourceSchemaKey(descriptor.ResourceTypeSegments); if (providerSchema.ResourceDefinitions.ContainsKey(resourcePropertyName)) { LogMessage($"Skipping resource type {descriptor.FullyQualifiedType} under path '{method.Url}': Duplicate resource definition {resourcePropertyName}"); continue; } providerSchema.AddResourceDefinition(resourcePropertyName, new ResourceDefinition { Descriptor = descriptor, Schema = resourceSchema, }); } } // 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 (var providerSchema in providerSchemas.Values) { // Sort by resource length to process parent resources before children var childResourceDefinitions = providerSchema.ResourceDefinitions.Values .Where(resource => resource.Descriptor.ResourceTypeSegments.Count() > 1) .OrderBy(resource => resource.Descriptor.ResourceTypeSegments.Count()); foreach (var childResource in childResourceDefinitions) { var parentTypeSegments = childResource.Descriptor.ResourceTypeSegments.SkipLast(1); if (!providerSchema.GetResourceDefinitionByResourceType(parentTypeSegments, out var parentResource)) { continue; } var childResourceSchema = childResource.Schema.Clone(); childResourceSchema.ResourceType = childResource.Descriptor.ResourceTypeSegments.Last(); var childResourceDefinitionName = ResourceSchema.FormatResourceSchemaKey(childResource.Descriptor.ResourceTypeSegments) + "_childResource"; providerSchema.AddDefinition(childResourceDefinitionName, childResourceSchema); if (!parentResource.Schema.Properties.ContainsKey("resources")) { parentResource.Schema.AddProperty("resources", new JsonSchema { JsonType = "array", Items = new JsonSchema() }); } parentResource.Schema.Properties["resources"].Items.AddOneOf(new JsonSchema { Ref = "#/definitions/" + childResourceDefinitionName, }); } } return(providerSchemas); }
private static (bool success, string failureReason, JsonSchema nameSchema) ParseNameSchema(CodeModel codeModel, Method method, ResourceSchema providerSchema, ResourceDescriptor descriptor) { // get the resource name parameter, e.g. {fooName} var resNameParam = descriptor.RoutingScope.Substring(descriptor.RoutingScope.LastIndexOf('/') + 1); if (IsPathVariable(resNameParam)) { // strip the enclosing braces resNameParam = TrimParamBraces(resNameParam); // look up the type var param = method.Parameters.FirstOrDefault(p => p.SerializedName == resNameParam); if (param == null) { return(false, $"Unable to locate parameter with name '{resNameParam}'", null); } var nameSchema = ParseType(param.ClientProperty, param.ModelType, providerSchema.Definitions, codeModel.ModelTypes); nameSchema.ResourceType = resNameParam; return(true, string.Empty, nameSchema); } if (!resNameParam.All(c => char.IsLetterOrDigit(c))) { return(false, $"Unable to process non-alphanumeric name '{resNameParam}'", null); } // Resource name is a constant; enforce it with regex. var pattern = descriptor.ResourceTypeSegments.Count > 1 ? $"^.*/{resNameParam}$" : $"^{resNameParam}$"; return(true, string.Empty, new JsonSchema { JsonType = "string", Pattern = pattern, }); }
private static IDictionary <string, JsonSchema> GetResourceDefinitions(ResourceSchema resourceSchema, ScopeType scopeType) => resourceSchema.ResourceDefinitions .Where(kvp => kvp.Value.Descriptor.ScopeType == scopeType) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Schema);
/// <summary> /// Parse a ResourceSchemaModel from the provided ServiceClient. /// </summary> /// <param name="serviceClient"></param> /// <returns></returns> public static IDictionary <string, ResourceSchema> Parse(CodeModel serviceClient, string version) { if (serviceClient == null) { throw new ArgumentNullException(nameof(serviceClient)); } Dictionary <string, ResourceSchema> resourceSchemas = new Dictionary <string, ResourceSchema>(); foreach (Method method in serviceClient.Methods.Where(method => method.Parameters.FirstOrDefault(p => p.SerializedName == "api-version")?.DefaultValue.Value == version || version == serviceClient.ApiVersion)) { if (method.HttpMethod != HttpMethod.Put || string.IsNullOrWhiteSpace(method.Url) || !method.Url.Value.StartsWith(resourceMethodPrefix, StringComparison.OrdinalIgnoreCase) || !method.Url.Value.EndsWith("}", StringComparison.OrdinalIgnoreCase)) { continue; } string afterPrefix = method.Url.Value.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; } // extract API version string apiVersion = serviceClient.ApiVersion.Else(method.Parameters.FirstOrDefault(p => p.SerializedName == "api-version")?.DefaultValue); if (string.IsNullOrWhiteSpace(apiVersion)) { throw new ArgumentException("No API version is provided in the swagger document or the method."); } ResourceSchema resourceSchema; if (!resourceSchemas.ContainsKey(resourceProvider)) { resourceSchema = new ResourceSchema(); resourceSchema.Id = string.Format(CultureInfo.InvariantCulture, "https://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.ResourceType = resourceType; // get the resource name parameter, e.g. {fooName} var resNameParam = methodUrlPathAfterProvider.Substring(methodUrlPathAfterProvider.LastIndexOf('/') + 1); if (IsPathVariable(resNameParam)) { // strip the enclosing braces resNameParam = resNameParam.Trim(new[] { '{', '}' }); // look up the type var param = method.Parameters.Where(p => p.SerializedName == resNameParam).FirstOrDefault(); if (param != null) { // create a schema for it var nameParamSchema = ParseType(param.ClientProperty, param.ModelType, resourceSchema.Definitions, serviceClient.ModelTypes); nameParamSchema.ResourceType = resNameParam; // add it as the name property resourceDefinition.AddProperty("name", nameParamSchema, true); } } resourceDefinition.AddProperty("type", JsonSchema.CreateStringEnum(resourceType), true); resourceDefinition.AddProperty("apiVersion", JsonSchema.CreateStringEnum(apiVersion), true); if (method.Body != null) { CompositeType body = method.Body.ModelType 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.ComposedProperties) { if (!resourceDefinition.Properties.Keys.Contains(property.SerializedName)) { JsonSchema propertyDefinition = ParseType(property, property.ModelType, resourceSchema.Definitions, serviceClient.ModelTypes); if (propertyDefinition != null) { resourceDefinition.AddProperty(property.SerializedName, propertyDefinition, property.IsRequired || property.SerializedName == "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); }
/// <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.ComposedProperties) { 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 ResourceMarkdownWriter(TextWriter writer, ResourceSchema resourceSchema) { _writer = new MarkdownWriter(writer); _schema = resourceSchema; }