Skip to content

Commit

Permalink
OWIN Swashbuckle extensions converted to new project system
Browse files Browse the repository at this point in the history
Swashbuckle extension projects - Nuget metadata added
  • Loading branch information
tomasherceg committed May 24, 2018
1 parent 63e3575 commit 87d291d
Show file tree
Hide file tree
Showing 15 changed files with 378 additions and 97 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>DotVVM.Api.Swashbuckle.AspNetCore</PackageId>
<PackageVersion>2.0.0-preview08-28084</PackageVersion>
<Authors>RIGANTI</Authors>
<Description>Swashbuckle.AspNetCore extensions for DotVVM.</Description>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageTags>dotvvm;asp.net;mvvm;owin;dotnetcore;dnx</PackageTags>
<PackageIconUrl>https://dotvvm.com/Content/images/icons/icon-blue-64x64.png</PackageIconUrl>
<PackageLicenseUrl>https://github.com/riganti/dotvvm/blob/master/LICENSE</PackageLicenseUrl>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
<Name>DotVVM.Core</Name>
</ProjectReference>
<ProjectReference Include="..\DotVVM.Framework.Api.Swashbuckle.Owin\DotVVM.Framework.Api.Swashbuckle.Owin.csproj">
<Project>{54aecfe5-af60-425e-a022-8233d276d3c6}</Project>
<Project>{bd72f6fc-a6ca-4b10-8098-3d27693e25ba}</Project>
<Name>DotVVM.Framework.Api.Swashbuckle.Owin</Name>
</ProjectReference>
<ProjectReference Include="..\DotVVM.Samples.BasicSamples.Api.Common\DotVVM.Samples.BasicSamples.Api.Common.csproj">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace DotVVM.Framework.Api.Swashbuckle.Attributes
{
/// <summary>
/// Configure Swagger generator not to generate the method with the properties of the complex-type parameter passed as separate arguments, but to generate a method which accepts one argument of the specified complex type.
/// This attribute is used together with the FromUri attribute.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public class AsObjectAttribute : Attribute
{

public Type ClientType { get; set; }

public AsObjectAttribute()
{
}

public AsObjectAttribute(Type clientType)
{
ClientType = clientType;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{54AECFE5-AF60-425E-A022-8233D276D3C6}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DotVVM.Framework.Api.Swashbuckle.Owin</RootNamespace>
<AssemblyName>DotVVM.Framework.Api.Swashbuckle.Owin</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.0.0\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Options, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Options.1.0.0\lib\netstandard1.0\Microsoft.Extensions.Options.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Primitives, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Primitives.1.0.0\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Swashbuckle.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cd1bb07a5ac7c7bc, processorArchitecture=MSIL">
<HintPath>..\packages\Swashbuckle.Core.5.6.0\lib\net40\Swashbuckle.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http.Formatting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Attributes\AsObjectAttribute.cs" />
<Compile Include="Filters\AddAsObjectAnnotationOperationFilter.cs" />
<Compile Include="Filters\HandleKnownTypesDocumentFilter.cs" />
<Compile Include="Filters\AddTypeToModelSchemaFilter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SwashbuckleExtensions.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DotVVM.Core\DotVVM.Core.csproj">
<Project>{45fa2f43-d92b-4a9b-9b93-9ab519ad0127}</Project>
<Name>DotVVM.Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Linq;
using System.Reflection;
using System.Web.Http.Description;
using DotVVM.Core.Common;
using DotVVM.Framework.Api.Swashbuckle.Attributes;
using DotVVM.Framework.ViewModel;
using Swashbuckle.Swagger;

namespace DotVVM.Framework.Api.Swashbuckle.Owin.Filters
{
public class AddAsObjectAnnotationOperationFilter : IOperationFilter
{
private readonly IPropertySerialization propertySerialization;

public AddAsObjectAnnotationOperationFilter(IPropertySerialization propertySerialization)
{
this.propertySerialization = propertySerialization;
}

public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var parameters = apiDescription.ParameterDescriptions
.Select(p => new {
Value = p,
Attribute = p.ParameterDescriptor
.GetCustomAttributes<AsObjectAttribute>()
.FirstOrDefault()
})
.Where(d => d.Attribute != null);

var groups = apiDescription.ParameterDescriptions.GroupBy(p => p.ParameterDescriptor);

foreach (var param in parameters)
{
// add full type name to the metadata
foreach (var jsonParam in operation.parameters.Where(p => p.name.StartsWith(param.Value.Name + ".")))
{
var parameterType = param.Attribute.ClientType ?? param.Value.ParameterDescriptor.ParameterType;

// the vendorExtensions dictionary instance is reused, create a new one
var dict = jsonParam.vendorExtensions.ToDictionary(e => e.Key, e => e.Value);
dict.Add(ApiConstants.DotvvmWrapperTypeKey, parameterType.FullName + ", " + parameterType.Assembly.GetName().Name);
jsonParam.vendorExtensions = dict;

// fix casing in the second part of the name
var propertyName = GetPropertyName(parameterType, jsonParam.name.Substring(jsonParam.name.IndexOf(".") + 1));
jsonParam.name = param.Value.Name + "." + propertyName;
}
}
}

private string GetPropertyName(Type type, string propertyName)
{
var propertyInfo = type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

if (propertyInfo == null)
{
return propertyName;
}

return propertySerialization.ResolveName(propertyInfo);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Reflection;
using DotVVM.Core.Common;
using DotVVM.Framework.Controls;
using DotVVM.Framework.ViewModel;
using Swashbuckle.Swagger;

namespace DotVVM.Framework.Api.Swashbuckle.Owin.Filters
{
public class AddTypeToModelSchemaFilter : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
if (schema.type == "object")
{
schema.vendorExtensions.Add(ApiConstants.DotvvmTypeKey, type);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;
using System.Linq;
using System.Reflection;
using System.Web.Http.Description;
using DotVVM.Core.Common;
using DotVVM.Framework.ViewModel;
using Swashbuckle.Swagger;

namespace DotVVM.Framework.Api.Swashbuckle.Owin.Filters
{
public class HandleKnownTypesDocumentFilter : IDocumentFilter
{
private readonly DotvvmApiOptions apiOptions;
private readonly IPropertySerialization propertySerialization;

public HandleKnownTypesDocumentFilter(DotvvmApiOptions apiOptions, IPropertySerialization propertySerialization)
{
this.apiOptions = apiOptions;
this.propertySerialization = propertySerialization;
}

public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
foreach (var schema in swaggerDoc.definitions.Values)
{
if (schema.vendorExtensions.TryGetValue(ApiConstants.DotvvmTypeKey, out var objType) && objType is Type underlayingType)
{
if (apiOptions.IsKnownType(underlayingType))
{
var name = CreateProperName(underlayingType, swaggerDoc);
schema.vendorExtensions.Add(ApiConstants.DotvvmKnownTypeKey, name);

SetDotvvmNameToProperties(schema, underlayingType);
}
}
}

foreach (var definition in swaggerDoc.definitions)
{
definition.Value.vendorExtensions.Remove(ApiConstants.DotvvmTypeKey);
}
}

private void SetDotvvmNameToProperties(Schema schema, Type underlayingType)
{
if (schema.properties == null)
{
return;
}

foreach (var property in schema.properties)
{
SetDotvvmNameToProperty(underlayingType, property.Key, property.Value);
}
}

private void SetDotvvmNameToProperty(Type type, string propertyName, Schema targetSchema)
{
var propertyInfo = type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

if (propertyInfo != null)
{
targetSchema.vendorExtensions.Add(ApiConstants.DotvvmNameKey, propertySerialization.ResolveName(propertyInfo));
}
}

public string CreateProperName(Type type, SwaggerDocument swaggerDoc)
{
if (type.GetGenericArguments().Length == 0)
{
return CreateNameWithNamespace(type);
}

var genericArguments = type.GetGenericArguments().Select(t => CreateNameForGenericParameter(t, swaggerDoc));
var unmangledName = GetNameWithoutGenericArity(type);

return type.Namespace + '.' + unmangledName + '<' + string.Join(",", genericArguments) + '>';
}

public string CreateNameForGenericParameter(Type type, SwaggerDocument swaggerDoc)
{
var definition = swaggerDoc.definitions
.Where(d => d.Value.vendorExtensions.TryGetValue(ApiConstants.DotvvmTypeKey, out var objType) && (Type)objType == type)
.FirstOrDefault();

return definition.Key ?? type.FullName;
}

public static string GetNameWithoutGenericArity(Type type) => type.Name.Substring(0, type.Name.IndexOf('`'));

private static string CreateNameWithNamespace(Type type) => type.Namespace + '.' + type.Name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using DotVVM.Core.Common;
using DotVVM.Framework.Api.Swashbuckle.Owin.Filters;
using DotVVM.Framework.ViewModel;
using Swashbuckle.Application;
using Swashbuckle.Swagger;

namespace DotVVM.Framework.Api.Swashbuckle.Owin
{
public static class SwashbuckleExtensions
{
/// <summary>
/// Confgures Swaschbuckle to provide additional metadata in methods which use FromQuery attribute so the API provided by DotVVM API generator is easier to use.
/// </summary>
public static void EnableDotvvmIntegration(this SwaggerDocsConfig options, Action<DotvvmApiOptions> configureOptions = null)
{
var apiOptions = new DotvvmApiOptions();
configureOptions?.Invoke(apiOptions);

var propertySerialization = new DefaultPropertySerialization();
options.OperationFilter(() => new AddAsObjectAnnotationOperationFilter(propertySerialization));
options.SchemaFilter(() => new AddTypeToModelSchemaFilter());
options.DocumentFilter(() => new HandleKnownTypesDocumentFilter(apiOptions, propertySerialization));
}
}
}
Loading

0 comments on commit 87d291d

Please sign in to comment.