Mutate API description parameter type from JsonPatchDocument to Operation[]

Addresses #5464
This commit is contained in:
Jass Bagga 2017-01-13 16:39:08 -08:00 committed by GitHub
parent f95d49c870
commit 07c22f2b29
17 changed files with 236 additions and 1 deletions

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Microsoft.AspNetCore.Mvc.ApiExplorer
@ -35,5 +36,10 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
/// Gets or sets the parameter type.
/// </summary>
public Type Type { get; set; }
/// <summary>
/// Gets or sets the parameter descriptor.
/// </summary>
public ParameterDescriptor ParameterDescriptor { get; set; }
}
}

View File

@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
/// </summary>
/// <remarks>
/// Will be null if the action returns no response, or if the response type is unclear. Use
/// <see cref="ProducesAttribute"/> or <see cref="ProducesResponseTypeAttribute"/> on an action method
/// <c>Microsoft.AspNetCore.Mvc.ProducesAttribute</c> or <c>Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute</c> on an action method
/// to specify a response type.
/// </remarks>
public Type Type { get; set; }

View File

@ -26,8 +26,16 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
/// </remarks>
int Order { get; }
/// <summary>
/// Creates or modifies <see cref="ApiDescription"/>s.
/// </summary>
/// <param name="context">The <see cref="ApiDescriptionProviderContext"/>.</param>
void OnProvidersExecuting(ApiDescriptionProviderContext context);
/// <summary>
/// Called after <see cref="IApiDescriptionProvider"/> implementations with higher <see cref="Order"/> values have been called.
/// </summary>
/// <param name="context">The <see cref="ApiDescriptionProviderContext"/>.</param>
void OnProvidersExecuted(ApiDescriptionProviderContext context);
}
}

View File

@ -688,6 +688,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
Name = GetName(containerName, bindingContext),
Source = source,
Type = bindingContext.ModelMetadata.ModelType,
ParameterDescriptor = Parameter,
};
}

View File

@ -3,9 +3,18 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
[assembly: AssemblyMetadata("Serviceable", "True")]
[assembly: NeutralResourcesLanguage("en-us")]
[assembly: AssemblyCompany("Microsoft Corporation.")]
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
[assembly: AssemblyProduct("Microsoft ASP.NET Core")]
[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionProvider))]
[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription))]
[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionProviderContext))]
[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription))]
[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo))]
[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiRequestFormat))]
[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseFormat))]
[assembly: TypeForwardedTo(typeof(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseType))]

View File

@ -0,0 +1,34 @@
[
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionProviderContext",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiRequestFormat",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseFormat",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseType",
"Kind": "Removal"
},
{
"OldTypeId": "public interface Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionProvider",
"Kind": "Removal"
}
]

View File

@ -0,0 +1,34 @@
[
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionProviderContext",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiRequestFormat",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseFormat",
"Kind": "Removal"
},
{
"OldTypeId": "public class Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseType",
"Kind": "Removal"
},
{
"OldTypeId": "public interface Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionProvider",
"Kind": "Removal"
}
]

View File

@ -3,6 +3,8 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Formatters.Json;
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
@ -73,6 +75,8 @@ namespace Microsoft.Extensions.DependencyInjection
{
services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcJsonMvcOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApiDescriptionProvider, JsonPatchOperationsArrayProvider>());
services.TryAddSingleton<JsonResultExecutor>();
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Reflection;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.JsonPatch.Operations;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Microsoft.AspNetCore.Mvc.Formatters.Json
{
/// <summary>
/// Implements a provider of <see cref="ApiDescription"/> to change parameters of
/// type <see cref="IJsonPatchDocument"/> to an array of <see cref="Operation"/>.
/// </summary>
public class JsonPatchOperationsArrayProvider : IApiDescriptionProvider
{
private readonly IModelMetadataProvider _modelMetadataProvider;
/// <summary>
/// Creates a new instance of <see cref="JsonPatchOperationsArrayProvider"/>.
/// </summary>
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
public JsonPatchOperationsArrayProvider(IModelMetadataProvider modelMetadataProvider)
{
_modelMetadataProvider = modelMetadataProvider;
}
/// <inheritdoc />
/// <remarks>
/// The order -999 ensures that this provider is executed right after the <c>Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider</c>.
/// </remarks>
public int Order
{
get { return -999; }
}
/// <inheritdoc />
public void OnProvidersExecuting(ApiDescriptionProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
foreach (var result in context.Results)
{
foreach (var parameterDescription in result.ParameterDescriptions)
{
if (typeof(IJsonPatchDocument).GetTypeInfo().IsAssignableFrom(parameterDescription.Type))
{
parameterDescription.Type = typeof(Operation[]);
parameterDescription.ModelMetadata = _modelMetadataProvider.GetMetadataForType(typeof(Operation[]));
}
}
}
}
/// <inheritdoc />
public void OnProvidersExecuted(ApiDescriptionProviderContext context)
{
}
}
}

View File

@ -216,6 +216,22 @@ namespace Microsoft.AspNetCore.Mvc.Description
}
}
[Fact]
public void GetApiDescription_ParameterDescription_IncludesParameterDescriptor()
{
// Arrange
var action = CreateActionDescriptor(nameof(FromBody));
// Act
var descriptions = GetApiDescriptions(action);
// Assert
var description = Assert.Single(descriptions);
var parameterDescription = Assert.Single(description.ParameterDescriptions);
var actionParameterDescriptor = Assert.Single(action.Parameters);
Assert.Equal(actionParameterDescriptor, parameterDescription.ParameterDescriptor);
}
// Only a parameter which comes from a route or model binding or unknown should
// include route info.
[Theory]

View File

@ -0,0 +1,56 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.JsonPatch.Operations;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Formatters.Json
{
public class JsonPatchOperationsArrayProviderTests
{
[Fact]
public void OnProvidersExecuting_FindsJsonPatchDocuments_ProvidesOperationsArray()
{
// Arrange
var metadataprovider = new TestModelMetadataProvider();
var provider = new JsonPatchOperationsArrayProvider(metadataprovider);
var jsonPatchParameterDescription = new ApiParameterDescription
{
Type = typeof(JsonPatchDocument)
};
var stringParameterDescription = new ApiParameterDescription
{
Type = typeof(string),
};
var apiDescription = new ApiDescription();
apiDescription.ParameterDescriptions.Add(jsonPatchParameterDescription);
apiDescription.ParameterDescriptions.Add(stringParameterDescription);
var actionDescriptorList = new List<ActionDescriptor>();
var apiDescriptionProviderContext = new ApiDescriptionProviderContext(actionDescriptorList);
apiDescriptionProviderContext.Results.Add(apiDescription);
// Act
provider.OnProvidersExecuting(apiDescriptionProviderContext);
// Assert
Assert.Collection(apiDescription.ParameterDescriptions,
description =>
{
Assert.Equal(typeof(Operation[]), description.Type);
Assert.Equal(typeof(Operation[]), description.ModelMetadata.ModelType);
},
description =>
{
Assert.Equal(typeof(string), description.Type);
});
}
}
}

View File

@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Cors.Internal;
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Formatters.Json;
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Razor;
@ -411,6 +412,7 @@ namespace Microsoft.AspNetCore.Mvc
new Type[]
{
typeof(DefaultApiDescriptionProvider),
typeof(JsonPatchOperationsArrayProvider),
}
},
};