/// <summary> /// Creates an instance of <see cref="SchemaReferenceRegistry"/>. /// </summary> /// <param name="schemaGenerationSettings">The schema generation settings.</param> /// <param name="propertyDescriptionMap">The property description map.</param> public SchemaReferenceRegistry(SchemaGenerationSettings schemaGenerationSettings, Dictionary <string, string> propertyDescriptionMap) { _schemaGenerationSettings = schemaGenerationSettings ?? throw new ArgumentNullException(nameof(schemaGenerationSettings)); _propertyNameResolver = schemaGenerationSettings.PropertyNameResolver; _propertyDescriptionMap = propertyDescriptionMap ?? throw new ArgumentNullException(nameof(propertyDescriptionMap)); }
public ServiceConfigurator(IPropertyNameResolver propertyNameResolver) { _propertyNameResolver = propertyNameResolver; }
/// <summary> /// Finds the existing reference object based on the key from the input or creates a new one. /// </summary> /// <returns>The existing or created reference object.</returns> internal override OpenApiSchema FindOrAddReference(Type input) { // Return empty schema when the type does not have a name. // This can occur, for example, when a generic type without the generic argument specified // is passed in. if (input == null || input.FullName == null) { return(new OpenApiSchema()); } var key = GetKey(input); // If the schema already exists in the References, simply return. if (References.ContainsKey(key)) { return(new OpenApiSchema { Reference = new OpenApiReference { Id = key, Type = ReferenceType.Schema } }); } try { // There are multiple cases for input types that should be handled differently to match the OpenAPI spec. // // 1. Simple Type // 2. Enum Type // 3. Dictionary Type // 4. Enumerable Type // 5. Object Type var schema = new OpenApiSchema(); if (input.IsSimple()) { schema = input.MapToOpenApiSchema(); // Certain simple types yield more specific information. if (input == typeof(char)) { schema.MinLength = 1; schema.MaxLength = 1; } else if (input == typeof(Guid)) { schema.Example = new OpenApiString(Guid.Empty.ToString()); } return(schema); } if (input.IsEnum) { schema.Type = "string"; foreach (var name in Enum.GetNames(input)) { schema.Enum.Add(new OpenApiString(name)); } return(schema); } if (input.IsDictionary()) { schema.Type = "object"; schema.AdditionalProperties = FindOrAddReference(input.GetGenericArguments()[1]); return(schema); } if (input.IsEnumerable()) { schema.Type = "array"; schema.Items = FindOrAddReference(input.GetEnumerableItemType()); return(schema); } schema.Type = "object"; // Note this assignment is necessary to allow self-referencing type to finish // without causing stack overflow. // We can also assume that the schema is an object type at this point. References[key] = schema; var propertyNameDeclaringTypeMap = new Dictionary <string, Type>(); var typeAttributes = input.GetCustomAttributes(false); foreach (var typeAttribute in typeAttributes) { if (typeAttribute.GetType().FullName == "Newtonsoft.Json.JsonObjectAttribute") { var type = typeAttribute.GetType(); var namingStrategyInfo = type.GetProperty("NamingStrategyType"); if (namingStrategyInfo != null) { var namingStrategyValue = namingStrategyInfo.GetValue(typeAttribute, null); if (namingStrategyValue?.ToString() == "Newtonsoft.Json.Serialization.CamelCaseNamingStrategy") { _propertyNameResolver = new CamelCasePropertyNameResolver(); } } } } foreach (var propertyInfo in input.GetProperties()) { var ignoreProperty = false; var innerSchema = FindOrAddReference(propertyInfo.PropertyType); var propertyName = _propertyNameResolver.ResolvePropertyName(propertyInfo); var attributes = propertyInfo.GetCustomAttributes(false); foreach (var attribute in attributes) { if (attribute.GetType().FullName == "Newtonsoft.Json.JsonPropertyAttribute") { var type = attribute.GetType(); var requiredPropertyInfo = type.GetProperty("Required"); if (requiredPropertyInfo != null) { var requiredValue = Enum.GetName( requiredPropertyInfo.PropertyType, requiredPropertyInfo.GetValue(attribute, null)); if (requiredValue == "Always") { schema.Required.Add(propertyName); } } } if (attribute.GetType().FullName == "Newtonsoft.Json.JsonIgnoreAttribute") { ignoreProperty = true; } } if (ignoreProperty) { continue; } var propertyDeclaringType = propertyInfo.DeclaringType; if (propertyNameDeclaringTypeMap.ContainsKey(propertyName)) { var existingPropertyDeclaringType = propertyNameDeclaringTypeMap[propertyName]; var duplicateProperty = true; if (existingPropertyDeclaringType != null && propertyDeclaringType != null) { if (propertyDeclaringType.IsSubclassOf(existingPropertyDeclaringType) || (existingPropertyDeclaringType.IsInterface && propertyDeclaringType.ImplementInterface(existingPropertyDeclaringType))) { // Current property is on a derived class and hides the existing schema.Properties[propertyName] = innerSchema; duplicateProperty = false; } if (existingPropertyDeclaringType.IsSubclassOf(propertyDeclaringType) || (propertyDeclaringType.IsInterface && existingPropertyDeclaringType.ImplementInterface(propertyDeclaringType))) { // current property is hidden by the existing so don't add it continue; } } if (duplicateProperty) { throw new AddingSchemaReferenceFailedException( key, string.Format( SpecificationGenerationMessages.DuplicateProperty, propertyName, input)); } } schema.Properties[propertyName] = innerSchema; propertyNameDeclaringTypeMap.Add(propertyName, propertyDeclaringType); } References[key] = schema; return(new OpenApiSchema { Reference = new OpenApiReference { Id = key, Type = ReferenceType.Schema } }); } catch (Exception e) { // Something went wrong while fetching schema, so remove the key if exists from the references. if (References.ContainsKey(key)) { References.Remove(key); } throw new AddingSchemaReferenceFailedException(key, e.Message); } }
/// <summary> /// Creates an instance of <see cref="SchemaReferenceRegistry"/>. /// </summary> /// <param name="schemaGenerationSettings">The schema generation settings.</param> public SchemaReferenceRegistry(SchemaGenerationSettings schemaGenerationSettings) { _schemaGenerationSettings = schemaGenerationSettings ?? throw new ArgumentNullException(nameof(schemaGenerationSettings)); _propertyNameResolver = schemaGenerationSettings.PropertyNameResolver; }
/// <summary> /// Creates an instance of <see cref="SchemaGenerationSettings"/> class. /// </summary> /// <param name="propertyNameResolver">The property name resolver.</param> public SchemaGenerationSettings(IPropertyNameResolver propertyNameResolver) { PropertyNameResolver = propertyNameResolver ?? throw new ArgumentNullException(nameof(propertyNameResolver)); }