Last active
October 3, 2018 03:01
-
-
Save kpol/ba44b9f9f5b825d6fce96996f7831a15 to your computer and use it in GitHub Desktop.
Swashbuckle filters to add enum type into Swagger (OpenAPI) spec file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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