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, }); }