Skip to content

Instantly share code, notes, and snippets.

@kpol
Last active October 3, 2018 03:01
Show Gist options
  • Select an option

  • Save kpol/ba44b9f9f5b825d6fce96996f7831a15 to your computer and use it in GitHub Desktop.

Select an option

Save kpol/ba44b9f9f5b825d6fce96996f7831a15 to your computer and use it in GitHub Desktop.
Swashbuckle filters to add enum type into Swagger (OpenAPI) spec file.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.Description;
using Newtonsoft.Json.Serialization;
using Swashbuckle.Swagger;
namespace Swagger.Configurations
{
public class SwashbuckleDocument
{
private readonly DefaultContractResolver _contractResolver = new DefaultContractResolver();
public const string NullableExtension = "x-nullable";
public const string EnumTypeExtension = "x-enumType";
public const string EnumNameExtension = "x-enumName";
public const string ClassTypeExtension = "x-className";
public Type FindEnum(Type type, out bool isNullable)
{
isNullable = false;
while (true)
{
var contract = GetContract(type);
switch (contract)
{
case JsonPrimitiveContract primitiveContract when primitiveContract.UnderlyingType.IsEnum:
return primitiveContract.UnderlyingType;
case JsonPrimitiveContract primitiveContract when primitiveContract.UnderlyingType.IsGenericType:
var t = Nullable.GetUnderlyingType(primitiveContract.UnderlyingType);
if (t != null && t.IsEnum)
{
isNullable = true;
return t;
}
return null;
case JsonArrayContract arrayContract:
type = arrayContract.CollectionItemType;
continue;
case JsonDictionaryContract dictionaryContract:
type = dictionaryContract.DictionaryValueType;
continue;
default:
return null;
}
}
}
public JsonContract GetContract(Type type)
{
return _contractResolver.ResolveContract(type);
}
public void UpdateSchemaEnum(Schema schema, Type type, bool isNullable)
{
while (schema.@enum == null)
{
if (schema.items != null)
{
schema = schema.items;
continue;
}
if (schema.additionalProperties != null)
{
schema = schema.additionalProperties;
continue;
}
break;
}
if (schema.@enum != null)
{
AddEnumVendorExtensions(schema.vendorExtensions, type, isNullable);
}
}
public void AddEnumVendorExtensions(IDictionary<string, object> vendorExtensions, Type enumType, bool isNullable)
{
if (!vendorExtensions.ContainsKey(EnumNameExtension))
{
vendorExtensions.Add(EnumNameExtension, enumType.Name);
}
if (!vendorExtensions.ContainsKey(EnumTypeExtension))
{
vendorExtensions.Add(EnumTypeExtension, enumType.FullName);
}
if (isNullable && !vendorExtensions.ContainsKey(NullableExtension))
{
vendorExtensions.Add(NullableExtension, true);
}
}
public void AddClassVendorExtensions(IDictionary<string, object> vendorExtensions, Type classType)
{
if (!vendorExtensions.ContainsKey(ClassTypeExtension))
{
vendorExtensions.Add(ClassTypeExtension, classType.Name);
}
}
}
public class EnumSchemaFilter : ISchemaFilter
{
private readonly SwashbuckleDocument _document;
public EnumSchemaFilter(SwashbuckleDocument document)
{
_document = document;
}
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
_document.AddClassVendorExtensions(schema.vendorExtensions, type);
if (schema.properties != null)
{
foreach (var property in ((JsonObjectContract)_document.GetContract(type)).Properties)
{
var t = _document.FindEnum(property.PropertyType, out var isNullable);
if (t != null)
{
var p = schema.properties.FirstOrDefault(k =>
StringComparer.OrdinalIgnoreCase.Equals(k.Key, property.PropertyName));
if (p.Key != null)
{
var propertySchema = p.Value;
_document.UpdateSchemaEnum(propertySchema, t, isNullable);
}
}
}
}
}
}
public class EnumOperationFilter : IOperationFilter
{
private readonly SwashbuckleDocument _document;
public EnumOperationFilter(SwashbuckleDocument document)
{
_document = document;
}
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation != null)
{
if (operation.parameters != null)
{
var enums = new Dictionary<string, Tuple<Type, bool>>();
GetAllEnumsFromParameters(apiDescription, enums);
if (enums.Count > 0)
{
foreach (var operationParameter in operation.parameters)
{
if (enums.TryGetValue(operationParameter.name, out var type))
{
if (operationParameter.@enum != null)
{
_document.AddEnumVendorExtensions(operationParameter.vendorExtensions, type.Item1, type.Item2);
}
else if (operationParameter.schema != null)
{
_document.UpdateSchemaEnum(operationParameter.schema, type.Item1, type.Item2);
}
}
}
}
}
UpdateResponse(apiDescription, operation);
}
}
private void GetAllEnumsFromParameters(ApiDescription apiDescription,
IDictionary<string, Tuple<Type, bool>> enums)
{
if (apiDescription.ParameterDescriptions != null)
{
foreach (var p in apiDescription.ParameterDescriptions)
{
var enumType = _document.FindEnum(p.ParameterDescriptor.ParameterType, out var isNullable);
if (enumType != null && !enums.ContainsKey(p.Name))
{
enums.Add(p.Name, new Tuple<Type, bool>(enumType, isNullable));
}
}
}
}
private void UpdateResponse(ApiDescription apiDescription, Operation operation)
{
if (apiDescription.ResponseDescription.DeclaredType != null)
{
var type = _document.FindEnum(apiDescription.ResponseDescription.DeclaredType, out var isNullable);
if (type != null && operation.responses != null)
{
foreach (var response in operation.responses)
{
if (response.Value?.schema != null)
{
_document.UpdateSchemaEnum(response.Value.schema, type, isNullable);
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment