Mutate API description parameter type from JsonPatchDocument to Operation[]
Addresses #5464
This commit is contained in:
parent
f95d49c870
commit
07c22f2b29
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
|
|
@ -35,5 +36,10 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
/// Gets or sets the parameter type.
|
/// Gets or sets the parameter type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Type Type { get; set; }
|
public Type Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the parameter descriptor.
|
||||||
|
/// </summary>
|
||||||
|
public ParameterDescriptor ParameterDescriptor { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Will be null if the action returns no response, or if the response type is unclear. Use
|
/// 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.
|
/// to specify a response type.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public Type Type { get; set; }
|
public Type Type { get; set; }
|
||||||
|
|
@ -26,8 +26,16 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
int Order { get; }
|
int Order { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates or modifies <see cref="ApiDescription"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The <see cref="ApiDescriptionProviderContext"/>.</param>
|
||||||
void OnProvidersExecuting(ApiDescriptionProviderContext context);
|
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);
|
void OnProvidersExecuted(ApiDescriptionProviderContext context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -688,6 +688,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
Name = GetName(containerName, bindingContext),
|
Name = GetName(containerName, bindingContext),
|
||||||
Source = source,
|
Source = source,
|
||||||
Type = bindingContext.ModelMetadata.ModelType,
|
Type = bindingContext.ModelMetadata.ModelType,
|
||||||
|
ParameterDescriptor = Parameter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,18 @@
|
||||||
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Resources;
|
using System.Resources;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: AssemblyMetadata("Serviceable", "True")]
|
[assembly: AssemblyMetadata("Serviceable", "True")]
|
||||||
[assembly: NeutralResourcesLanguage("en-us")]
|
[assembly: NeutralResourcesLanguage("en-us")]
|
||||||
[assembly: AssemblyCompany("Microsoft Corporation.")]
|
[assembly: AssemblyCompany("Microsoft Corporation.")]
|
||||||
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
|
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
|
||||||
[assembly: AssemblyProduct("Microsoft ASP.NET Core")]
|
[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))]
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Formatters.Json;
|
||||||
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
|
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
@ -73,6 +75,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcJsonMvcOptionsSetup>());
|
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcJsonMvcOptionsSetup>());
|
||||||
|
services.TryAddEnumerable(
|
||||||
|
ServiceDescriptor.Transient<IApiDescriptionProvider, JsonPatchOperationsArrayProvider>());
|
||||||
services.TryAddSingleton<JsonResultExecutor>();
|
services.TryAddSingleton<JsonResultExecutor>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
// Only a parameter which comes from a route or model binding or unknown should
|
||||||
// include route info.
|
// include route info.
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Cors.Internal;
|
using Microsoft.AspNetCore.Mvc.Cors.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
|
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Formatters.Json;
|
||||||
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
|
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.Internal;
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor;
|
using Microsoft.AspNetCore.Mvc.Razor;
|
||||||
|
|
@ -411,6 +412,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
new Type[]
|
new Type[]
|
||||||
{
|
{
|
||||||
typeof(DefaultApiDescriptionProvider),
|
typeof(DefaultApiDescriptionProvider),
|
||||||
|
typeof(JsonPatchOperationsArrayProvider),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue