Skip to content

Instantly share code, notes, and snippets.

@danslapman
Last active June 19, 2023 06:45
Show Gist options
  • Save danslapman/5ac4f9bc6fd69b5d36f3fae15d940c94 to your computer and use it in GitHub Desktop.
Save danslapman/5ac4f9bc6fd69b5d36f3fae15d940c94 to your computer and use it in GitHub Desktop.
Basic F# support for Swashbuckle
public static class GenericExtensions
{
public static void CopyProperties<T>(this T source, T destination)
{
var props = source?.GetType().GetProperties();
if (props == null) return;
foreach (var prop in props)
{
prop.SetValue(destination, prop.GetValue(source));
}
}
}
using Microsoft.FSharp.Core;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
public class OptionSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.Type.IsGenericType && context.Type.GetGenericTypeDefinition() == typeof(FSharpOption<>))
{
var underlyingType = context.Type.GetGenericArguments()[0]!;
if (context.SchemaRepository.TryLookupByType(underlyingType, out var innerSchema))
{
innerSchema.CopyProperties(schema);
}
else if (underlyingType.IsGenericType && underlyingType.ImplementsInterface(typeof(IEnumerable<>)))
{
var paramType = underlyingType.GetGenericArguments()[0]!;
if (context.SchemaRepository.TryLookupByType(typeof(IEnumerable<>).MakeGenericType(paramType), out var collSchema))
{
collSchema.CopyProperties(collSchema);
}
}
else
{
switch (underlyingType)
{
case var str when str == typeof(string):
schema.Title = "string";
schema.Type = "string";
break;
case var dec when dec == typeof(decimal):
schema.Title = "number";
schema.Type = "number";
break;
}
}
}
}
}
using System.Reflection;
using LanguageExt;
using Microsoft.FSharp.Collections;
using Microsoft.FSharp.Core;
using Microsoft.FSharp.Reflection;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
public class UnionSchemaFilter : ISchemaFilter
{
private static readonly Seq<Type> BuiltInTypes = Prelude.Seq(
typeof(FSharpOption<>),
typeof(FSharpList<>),
typeof(FSharpMap<,>)
);
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if ((!context.Type.IsGenericType || !BuiltInTypes.Exists(t => context.Type.GetGenericTypeDefinition() == t)) && FSharpType.IsUnion(context.Type, null))
{
var cases = FSharpType.GetUnionCases(context.Type, null);
foreach (var c in cases)
{
var caseFields = c.GetFields();
if (caseFields.Length() > 0)
{
var caseType = caseFields[0].ReflectedType?.GetTypeInfo().DeclaredFields.FirstOrDefault()?.FieldType;
var caseSchema = context.SchemaGenerator.GenerateSchema(caseType, context.SchemaRepository);
var wrapped = new OpenApiSchema().Tap(s => s.Properties.Add(c.Name, caseSchema));
schema.OneOf.Add(wrapped);
}
}
}
}
}
@danslapman
Copy link
Author

My bad, forgot GenericExtensions class

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment