diff --git a/src/Swagger for ServiceComposer/ApiDescription/ServiceComposerApiDescriptionProvider.cs b/src/Swagger for ServiceComposer/ApiDescription/ServiceComposerApiDescriptionProvider.cs index a309a81..490a4da 100644 --- a/src/Swagger for ServiceComposer/ApiDescription/ServiceComposerApiDescriptionProvider.cs +++ b/src/Swagger for ServiceComposer/ApiDescription/ServiceComposerApiDescriptionProvider.cs @@ -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; @@ -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 { @@ -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()) + foreach (var producesDefaultResponseTypeAttribute in routeEndpoint.Metadata + .OfType()) { 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()) + foreach (var producesResponseTypeAttribute in routeEndpoint.Metadata.OfType() + ) { apiDescription.SupportedResponseTypes.Add(new ApiResponseType { @@ -84,7 +87,8 @@ private Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription CreateApiDescription }); } - foreach (var apiParameterDescriptionAttribute in routeEndpoint.Metadata.OfType()) + foreach (var apiParameterDescriptionAttribute in routeEndpoint.Metadata + .OfType()) { apiDescription.ParameterDescriptions.Add(new ApiParameterDescription() { @@ -96,12 +100,43 @@ private Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription CreateApiDescription }); } + // var typedViewModelAttributes = routeEndpoint.Metadata + // .OfType() + // .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; @@ -112,4 +147,9 @@ public void OnProvidersExecuted(ApiDescriptionProviderContext context) // no-op } } + + public abstract class ComposedViewModel + { + + } } \ No newline at end of file diff --git a/src/Swagger for ServiceComposer/Handlers/Documentation/DocumentationHandler.cs b/src/Swagger for ServiceComposer/Handlers/Documentation/DocumentationHandler.cs index 15e5c44..95d7168 100644 --- a/src/Swagger for ServiceComposer/Handlers/Documentation/DocumentationHandler.cs +++ b/src/Swagger for ServiceComposer/Handlers/Documentation/DocumentationHandler.cs @@ -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) diff --git a/src/Swagger for ServiceComposer/Handlers/ServiceA/SampleHandler.cs b/src/Swagger for ServiceComposer/Handlers/ServiceA/SampleHandler.cs index 3fbe8e5..4268bbf 100644 --- a/src/Swagger for ServiceComposer/Handlers/ServiceA/SampleHandler.cs +++ b/src/Swagger for ServiceComposer/Handlers/ServiceA/SampleHandler.cs @@ -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(); vm.AValue = id; return Task.CompletedTask; diff --git a/src/Swagger for ServiceComposer/Handlers/ServiceB/SampleHandler.cs b/src/Swagger for ServiceComposer/Handlers/ServiceB/SampleHandler.cs index eb37942..7a93ef3 100644 --- a/src/Swagger for ServiceComposer/Handlers/ServiceB/SampleHandler.cs +++ b/src/Swagger for ServiceComposer/Handlers/ServiceB/SampleHandler.cs @@ -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(); vm.AnotherValue = "Hi, there."; return Task.CompletedTask; diff --git a/src/Swagger for ServiceComposer/Models/Response/IAValue.cs b/src/Swagger for ServiceComposer/Models/Response/IAValue.cs new file mode 100644 index 0000000..e4a8541 --- /dev/null +++ b/src/Swagger for ServiceComposer/Models/Response/IAValue.cs @@ -0,0 +1,7 @@ +namespace Swagger_for_ServiceComposer.Models.Response +{ + public interface IAValue + { + int AValue { get; set; } + } +} \ No newline at end of file diff --git a/src/Swagger for ServiceComposer/Models/Response/IAnotherValue.cs b/src/Swagger for ServiceComposer/Models/Response/IAnotherValue.cs new file mode 100644 index 0000000..40afef1 --- /dev/null +++ b/src/Swagger for ServiceComposer/Models/Response/IAnotherValue.cs @@ -0,0 +1,7 @@ +namespace Swagger_for_ServiceComposer.Models.Response +{ + public interface IAnotherValue + { + string AnotherValue { get; set; } + } +} \ No newline at end of file diff --git a/src/Swagger for ServiceComposer/Models/Response/Sample.cs b/src/Swagger for ServiceComposer/Models/Response/ViewModel.cs similarity index 75% rename from src/Swagger for ServiceComposer/Models/Response/Sample.cs rename to src/Swagger for ServiceComposer/Models/Response/ViewModel.cs index c992881..b38cf5c 100644 --- a/src/Swagger for ServiceComposer/Models/Response/Sample.cs +++ b/src/Swagger for ServiceComposer/Models/Response/ViewModel.cs @@ -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; } diff --git a/src/Swagger for ServiceComposer/Models/ViewModelFactory.cs b/src/Swagger for ServiceComposer/Models/ViewModelFactory.cs new file mode 100644 index 0000000..3f20fd5 --- /dev/null +++ b/src/Swagger for ServiceComposer/Models/ViewModelFactory.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/Swagger for ServiceComposer/Properties/launchSettings.json b/src/Swagger for ServiceComposer/Properties/launchSettings.json index 4bd9e0f..a6454bf 100644 --- a/src/Swagger for ServiceComposer/Properties/launchSettings.json +++ b/src/Swagger for ServiceComposer/Properties/launchSettings.json @@ -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" } diff --git a/src/Swagger for ServiceComposer/Startup.cs b/src/Swagger for ServiceComposer/Startup.cs index a2d885d..59b25b1 100644 --- a/src/Swagger for ServiceComposer/Startup.cs +++ b/src/Swagger for ServiceComposer/Startup.cs @@ -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 { @@ -14,6 +15,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddRouting(); services.AddViewModelComposition(); + services.AddSingleton(); services.AddSwaggerGen(); services.AddControllers(); // Swagger needs this for the ApiExplorer. diff --git a/src/Swagger for ServiceComposer/Swagger for ServiceComposer.csproj b/src/Swagger for ServiceComposer/Swagger for ServiceComposer.csproj index c5689ca..6550431 100644 --- a/src/Swagger for ServiceComposer/Swagger for ServiceComposer.csproj +++ b/src/Swagger for ServiceComposer/Swagger for ServiceComposer.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/nuget.config b/src/nuget.config new file mode 100644 index 0000000..fe14dfe --- /dev/null +++ b/src/nuget.config @@ -0,0 +1,10 @@ + + + + + + + + + +