Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typed view model response #3

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Routing;

namespace Swagger_for_ServiceComposer.ApiDescription
{
internal class ServiceComposerApiDescriptionProvider : IApiDescriptionProvider
{
// private static readonly ProxyGenerator ProxyGenerator = new ProxyGenerator();
private readonly EndpointDataSource _endpointDataSource;
private readonly IModelMetadataProvider _modelMetadataProvider;

Expand Down Expand Up @@ -45,7 +45,7 @@ private Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription CreateApiDescription
var verb = httpMethodMetadata?.HttpMethods.FirstOrDefault();

var apiDescription = new Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription();
// Default to a GET in case a Route map was registered inline - it's unlikely to be a composition handler in that case.
// Default to a GET in case a Route map was registered inline - it's unlikely to be a composition handler in that case.
apiDescription.HttpMethod = verb ?? "GET";
apiDescription.ActionDescriptor = new ActionDescriptor
{
Expand All @@ -54,25 +54,28 @@ private Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription CreateApiDescription
// Swagger uses this to group endpoints together.
// Group methods together using the service name.
// NOTE: Need a metadata model in service composer to begin supplying more info other than just http verbs and route patterns.
["controller"] = "ViewModelComposition"// routeEndpoint.RoutePattern.GetParameter("controller").Default.ToString()
["controller"] =
"ViewModelComposition" // routeEndpoint.RoutePattern.GetParameter("controller").Default.ToString()
}
};
apiDescription.RelativePath = routeEndpoint.RoutePattern.RawText.TrimStart('/');
apiDescription.SupportedRequestFormats.Add(new ApiRequestFormat { MediaType = "application/json" });
apiDescription.SupportedRequestFormats.Add(new ApiRequestFormat {MediaType = "application/json"});

foreach (var producesDefaultResponseTypeAttribute in routeEndpoint.Metadata.OfType<ProducesDefaultResponseTypeAttribute>())
foreach (var producesDefaultResponseTypeAttribute in routeEndpoint.Metadata
.OfType<ProducesDefaultResponseTypeAttribute>())
{
apiDescription.SupportedResponseTypes.Add(new ApiResponseType
{
Type = producesDefaultResponseTypeAttribute.Type,
ApiResponseFormats = { new ApiResponseFormat { MediaType = "application/json" } },
ApiResponseFormats = {new ApiResponseFormat {MediaType = "application/json"}},
StatusCode = producesDefaultResponseTypeAttribute.StatusCode,
IsDefaultResponse = true,
ModelMetadata = _modelMetadataProvider.GetMetadataForType(producesDefaultResponseTypeAttribute.Type)
});
}

foreach (var producesResponseTypeAttribute in routeEndpoint.Metadata.OfType<ProducesResponseTypeAttribute>())
foreach (var producesResponseTypeAttribute in routeEndpoint.Metadata.OfType<ProducesResponseTypeAttribute>()
)
{
apiDescription.SupportedResponseTypes.Add(new ApiResponseType
{
Expand All @@ -84,7 +87,8 @@ private Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription CreateApiDescription
});
}

foreach (var apiParameterDescriptionAttribute in routeEndpoint.Metadata.OfType<ApiParameterDescriptionAttribute>())
foreach (var apiParameterDescriptionAttribute in routeEndpoint.Metadata
.OfType<ApiParameterDescriptionAttribute>())
{
apiDescription.ParameterDescriptions.Add(new ApiParameterDescription()
{
Expand All @@ -96,12 +100,43 @@ private Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription CreateApiDescription
});
}

// var typedViewModelAttributes = routeEndpoint.Metadata
// .OfType<TypedViewModelAttribute>()
// .ToList();
// if (typedViewModelAttributes.Any())
// {
// var types = typedViewModelAttributes.Select(a => a.Type).Distinct();
//
// var ctor = typeof(DisplayNameAttribute).GetConstructor(new[] { typeof(string) });
// var options = new ProxyGenerationOptions()
// {
// AdditionalAttributes =
// {
// new CustomAttributeInfo(ctor, new object[]{"composed view model"})
// }
// };
// var vm = ProxyGenerator.CreateClassProxy(
// typeof(ComposedViewModel),
// types.ToArray(),
// options);
// var vmType = vm.GetType();
//
// apiDescription.SupportedResponseTypes.Add(new ApiResponseType
// {
// Type = vmType,
// ApiResponseFormats = {new ApiResponseFormat {MediaType = "application/json"}},
// StatusCode = 200,
// IsDefaultResponse = true,
// ModelMetadata = _modelMetadataProvider.GetMetadataForType(vmType)
// });
// }

return apiDescription;
}

BindingSource GetBindingSource(string source)
{
var staticProps = typeof(BindingSource).GetFields(BindingFlags.Static|BindingFlags.Public);
var staticProps = typeof(BindingSource).GetFields(BindingFlags.Static | BindingFlags.Public);
var prop = staticProps.Single(p => p.Name == source);

return prop.GetValue(null) as BindingSource;
Expand All @@ -112,4 +147,9 @@ public void OnProvidersExecuted(ApiDescriptionProviderContext context)
// no-op
}
}

public abstract class ComposedViewModel
{

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class DocumentationHandler : ICompositionRequestsHandler
{
[HttpGet("/sample/{id}")]
[ProducesDefaultResponseType(typeof(void))]
[ProducesResponseType(typeof(Sample), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ViewModel), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
[ApiParameterDescription(Name = "id", IsRequired = true, Type = typeof(int), Source = "Path")]
public Task Handle(HttpRequest request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using ServiceComposer.AspNetCore;
using Swagger_for_ServiceComposer.ApiDescription;
using Swagger_for_ServiceComposer.Models.Response;

namespace Swagger_for_ServiceComposer.Handlers.ServiceA
{
public class SampleHandler : ICompositionRequestsHandler
{
[HttpGet("/sample/{id}")]
[ApiParameterDescription(Name = "id", IsRequired = true, Source = "Path", Type = typeof(int))]
public Task Handle(HttpRequest request)
{
var routeData = request.HttpContext.GetRouteData();
var id = Convert.ToInt32(routeData.Values["id"]);

var vm = request.GetComposedResponseModel();
var vm = request.GetComposedResponseModel<IAValue>();
vm.AValue = id;

return Task.CompletedTask;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using ServiceComposer.AspNetCore;
using Swagger_for_ServiceComposer.ApiDescription;
using Swagger_for_ServiceComposer.Models.Response;

namespace Swagger_for_ServiceComposer.Handlers.ServiceB
{
public class SampleHandler : ICompositionRequestsHandler
{
[HttpGet("/sample/{id}")]
[ApiParameterDescription(Name = "id", IsRequired = true, Source = "Path", Type = typeof(int))]
public Task Handle(HttpRequest request)
{
var vm = request.GetComposedResponseModel();
var vm = request.GetComposedResponseModel<IAnotherValue>();
vm.AnotherValue = "Hi, there.";

return Task.CompletedTask;
Expand Down
7 changes: 7 additions & 0 deletions src/Swagger for ServiceComposer/Models/Response/IAValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Swagger_for_ServiceComposer.Models.Response
{
public interface IAValue
{
int AValue { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Swagger_for_ServiceComposer.Models.Response
{
public interface IAnotherValue
{
string AnotherValue { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Swagger_for_ServiceComposer.Models.Response
{
public class Sample
public class ViewModel : IAValue, IAnotherValue
{
public int AValue { get; set; }
public string AnotherValue { get; set; }
Expand Down
14 changes: 14 additions & 0 deletions src/Swagger for ServiceComposer/Models/ViewModelFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Http;
using ServiceComposer.AspNetCore;
using Swagger_for_ServiceComposer.Models.Response;

namespace Swagger_for_ServiceComposer.Models
{
public class ViewModelFactory : IViewModelFactory
{
public object CreateViewModel(HttpContext httpContext, ICompositionContext compositionContext)
{
return new ViewModel();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"Swagger_for_ServiceComposer": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
Expand Down
2 changes: 2 additions & 0 deletions src/Swagger for ServiceComposer/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using ServiceComposer.AspNetCore;
using Swagger_for_ServiceComposer.ApiDescription;
using Swagger_for_ServiceComposer.Models;

namespace Swagger_for_ServiceComposer
{
Expand All @@ -14,6 +15,7 @@ public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
services.AddViewModelComposition();
services.AddSingleton<IViewModelFactory, ViewModelFactory>();

services.AddSwaggerGen();
services.AddControllers(); // Swagger needs this for the ApiExplorer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ServiceComposer.AspNetCore" Version="1.5.0" />
<PackageReference Include="ServiceComposer.AspNetCore" Version="1.7.2-pr.155.build-id.36943360.alpha.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
</ItemGroup>

Expand Down
10 changes: 10 additions & 0 deletions src/nuget.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="DependencyVersion" value="HighestMinor" />
</config>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="ServiceComposer Unstable" value="https://www.myget.org/F/service-composer-unstable/api/v3/index.json" />
</packageSources>
</configuration>