private void FilterSchema(OpenApiSchema schema, OpenApiSchemaFilterContext schemaFilterContext) { foreach (var schemaFilter in _compilerConfiguration.SchemaFilters) { schemaFilter.Apply(schema, schemaFilterContext); } }
/// <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> public 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 = _compilerConfiguration.SchemaIdSelector(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(); // Filter schema var schemaFilterContext = new OpenApiSchemaFilterContext { Type = input }; 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()); } FilterSchema(schema, schemaFilterContext); return(schema); } if (input.IsEnum) { schema.Type = "string"; foreach (var name in Enum.GetNames(input)) { schema.Enum.Add(new OpenApiString(name)); } FilterSchema(schema, schemaFilterContext); return(schema); } if (input.IsDictionary()) { schema.Type = "object"; schema.AdditionalProperties = FindOrAddReference(input.GetGenericArguments()[1]); FilterSchema(schema, schemaFilterContext); return(schema); } if (input.IsEnumerable()) { schema.Type = "array"; schema.Items = FindOrAddReference(input.GetEnumerableItemType()); FilterSchema(schema, schemaFilterContext); 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; schemaFilterContext.PropertyNames = new Dictionary <string, string>(); foreach (var propertyInfo in input.GetProperties()) { // Ignore Property ? var ignoreProperty = propertyInfo.GetAttributeValue((JsonIgnoreAttribute attribute) => attribute) != null; if (!ignoreProperty) { ignoreProperty = propertyInfo.GetAttributeValue((SecurityPropertyAttribute attribute) => attribute) != null; } if (ignoreProperty) { continue; } // Property Name var propertyName = propertyInfo.GetAttributeValue((JsonPropertyAttribute attribute) => attribute.PropertyName); if (string.IsNullOrWhiteSpace(propertyName)) { propertyName = propertyInfo.GetAttributeValue((DataMemberAttribute attribute) => attribute.Name); } if (string.IsNullOrWhiteSpace(propertyName)) { propertyName = propertyInfo.Name.ToCamelCase(); } // Property Required var propertyRequired = propertyInfo.GetAttributeValue((JsonPropertyAttribute attribute) => attribute.Required) == Required.Always; if (!propertyRequired) { propertyRequired = propertyInfo.GetAttributeValue((RequiredAttribute attribute) => attribute) != null; } if (propertyRequired) { schema.Required.Add(propertyName); } // Min and max length int?minLength = propertyInfo.GetAttributeValue((MinLengthAttribute attribute) => attribute)?.Length; if (minLength == null) { minLength = propertyInfo.GetAttributeValue((StringLengthAttribute attribute) => attribute)?.MinimumLength; } int?maxLength = propertyInfo.GetAttributeValue((MaxLengthAttribute attribute) => attribute)?.Length; if (maxLength == null) { maxLength = propertyInfo.GetAttributeValue((StringLengthAttribute attribute) => attribute)?.MaximumLength; } // Regex pattern var pattern = propertyInfo.GetAttributeValue((RegularExpressionAttribute attribute) => attribute.Pattern); // Inner Schema var innerSchema = FindOrAddReference(propertyInfo.PropertyType); innerSchema.ReadOnly = !propertyInfo.CanWrite; innerSchema.MinLength = minLength; innerSchema.MaxLength = maxLength; innerSchema.Pattern = pattern; schema.Properties[propertyName] = innerSchema; schemaFilterContext.PropertyNames[propertyName] = propertyInfo.Name; } FilterSchema(schema, schemaFilterContext); _references[key] = schema; return(new OpenApiSchema { Reference = new OpenApiReference { Id = key, Type = ReferenceType.Schema } }); } catch (Exception) { // Something went wrong while fetching schema, so remove the key if exists from the references. if (_references.ContainsKey(key)) { _references.Remove(key); } throw; //throw new AddingSchemaReferenceFailedException(key, e.Message); } }