Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Mutate API description parameter type from JsonPatchDocument to Opera…
Browse files Browse the repository at this point in the history
…tion[]

Addresses #5464
  • Loading branch information
jbagga authored Jan 14, 2017
1 parent f95d49c commit 07c22f2
Show file tree
Hide file tree
Showing 17 changed files with 236 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -35,5 +36,10 @@ public class ApiParameterDescription
/// 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; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class ApiResponseType
/// </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; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,16 @@ public interface IApiDescriptionProvider
/// </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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ private ApiParameterDescription CreateResult(
Name = GetName(containerName, bindingContext),
Source = source,
Type = bindingContext.ModelMetadata.ModelType,
ParameterDescriptor = Parameter,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
34 changes: 34 additions & 0 deletions src/Microsoft.AspNetCore.Mvc.ApiExplorer/exceptions.net45.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
34 changes: 34 additions & 0 deletions src/Microsoft.AspNetCore.Mvc.ApiExplorer/exceptions.netcore.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -73,6 +75,8 @@ internal static void AddJsonFormatterServices(IServiceCollection services)
{
services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcJsonMvcOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApiDescriptionProvider, JsonPatchOperationsArrayProvider>());
services.TryAddSingleton<JsonResultExecutor>();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,22 @@ public void GetApiDescription_PopulatesParametersThatAppearOnRouteTemplate_AndHa
}
}

[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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
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;
Expand Down Expand Up @@ -411,6 +412,7 @@ private Dictionary<Type, Type[]> MutliRegistrationServiceTypes
new Type[]
{
typeof(DefaultApiDescriptionProvider),
typeof(JsonPatchOperationsArrayProvider),
}
},
};
Expand Down

0 comments on commit 07c22f2

Please sign in to comment.