Skip to content
Closed
20 changes: 19 additions & 1 deletion src/Features/JsonPatch.SystemTextJson/src/JsonPatchDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.JsonPatch.SystemTextJson.Adapters;
using Microsoft.AspNetCore.JsonPatch.SystemTextJson.Converters;
using Microsoft.AspNetCore.JsonPatch.SystemTextJson.Exceptions;
Expand All @@ -18,7 +21,7 @@ namespace Microsoft.AspNetCore.JsonPatch.SystemTextJson;
// documents for cases where there's no class/DTO to work on. Typical use case: backend not built in
// .NET or architecture doesn't contain a shared DTO layer.
[JsonConverter(typeof(JsonPatchDocumentConverter))]
public class JsonPatchDocument : IJsonPatchDocument
public class JsonPatchDocument : IJsonPatchDocument, IEndpointParameterMetadataProvider
{
public List<Operation> Operations { get; private set; }

Expand Down Expand Up @@ -218,4 +221,19 @@ IList<Operation> IJsonPatchDocument.GetOperations()

return allOps;
}

/// <summary>
/// Populates metadata for the related endpoint when this type is used as a parameter.
/// </summary>
/// <param name="parameter">The <see cref="ParameterInfo"/> for the endpoint parameter.</param>
/// <param name="builder">The endpoint builder for the endpoint being constructed.</param>
#pragma warning disable RS0016 // Add public types and members to the declared API
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
#pragma warning restore RS0016 // Add public types and members to the declared API
{
ArgumentNullException.ThrowIfNull(parameter);
ArgumentNullException.ThrowIfNull(builder);

builder.Metadata.Add(new AcceptsMetadata(new[] { "application/json-patch+json" }, parameter.ParameterType));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.JsonPatch.SystemTextJson.Adapters;
using Microsoft.AspNetCore.JsonPatch.SystemTextJson.Converters;
using Microsoft.AspNetCore.JsonPatch.SystemTextJson.Exceptions;
Expand All @@ -23,7 +25,7 @@ namespace Microsoft.AspNetCore.JsonPatch.SystemTextJson;
// including type data in the JsonPatchDocument serialized as JSON (to allow for correct deserialization) - that's
// not according to RFC 6902, and would thus break cross-platform compatibility.
[JsonConverter(typeof(JsonPatchDocumentConverterFactory))]
public class JsonPatchDocument<TModel> : IJsonPatchDocument where TModel : class
public class JsonPatchDocument<TModel> : IJsonPatchDocument, IEndpointParameterMetadataProvider where TModel : class
{
public List<Operation<TModel>> Operations { get; private set; }

Expand Down Expand Up @@ -657,6 +659,21 @@ IList<Operation> IJsonPatchDocument.GetOperations()
return allOps;
}

/// <summary>
/// Populates metadata for the related endpoint when this type is used as a parameter.
/// </summary>
/// <param name="parameter">The <see cref="ParameterInfo"/> for the endpoint parameter.</param>
/// <param name="builder">The endpoint builder for the endpoint being constructed.</param>
#pragma warning disable RS0016 // Add public types and members to the declared API
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
#pragma warning restore RS0016 // Add public types and members to the declared API
{
ArgumentNullException.ThrowIfNull(parameter);
ArgumentNullException.ThrowIfNull(builder);

builder.Metadata.Add(new AcceptsMetadata(new[] { "application/json-patch+json" }, parameter.ParameterType));
}

// Internal for testing
internal string GetPath<TProp>(Expression<Func<TModel, TProp>> expr, string position)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
<Compile Include="$(SharedSourceRoot)CallerArgument\CallerArgumentExpressionAttribute.cs" LinkBase="Shared" />
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Microsoft.AspNetCore.JsonPatch.SystemTextJson.Tests" />
</ItemGroup>
Expand Down
27 changes: 27 additions & 0 deletions src/Features/JsonPatch/src/JsonPatchDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNetCore.JsonPatch.Adapters;
using Microsoft.AspNetCore.JsonPatch.Converters;
using Microsoft.AspNetCore.JsonPatch.Exceptions;
Expand All @@ -12,13 +13,22 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

#if NET
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
#endif

namespace Microsoft.AspNetCore.JsonPatch;

// Implementation details: the purpose of this type of patch document is to allow creation of such
// documents for cases where there's no class/DTO to work on. Typical use case: backend not built in
// .NET or architecture doesn't contain a shared DTO layer.
[JsonConverter(typeof(JsonPatchDocumentConverter))]
#if NET
public class JsonPatchDocument : IJsonPatchDocument, IEndpointParameterMetadataProvider
#else
public class JsonPatchDocument : IJsonPatchDocument
#endif
{
public List<Operation> Operations { get; private set; }

Expand Down Expand Up @@ -218,4 +228,21 @@ IList<Operation> IJsonPatchDocument.GetOperations()

return allOps;
}

#if NET
/// <summary>
/// Populates metadata for the related endpoint when this type is used as a parameter.
/// </summary>
/// <param name="parameter">The <see cref="ParameterInfo"/> for the endpoint parameter.</param>
/// <param name="builder">The endpoint builder for the endpoint being constructed.</param>
#pragma warning disable RS0016 // Add public types and members to the declared API
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
#pragma warning restore RS0016 // Add public types and members to the declared API
{
ArgumentNullException.ThrowIfNull(parameter);
ArgumentNullException.ThrowIfNull(builder);

builder.Metadata.Add(new AcceptsMetadata(new[] { "application/json-patch+json" }, parameter.ParameterType));
}
#endif
}
27 changes: 27 additions & 0 deletions src/Features/JsonPatch/src/JsonPatchDocumentOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.AspNetCore.JsonPatch.Adapters;
using Microsoft.AspNetCore.JsonPatch.Converters;
using Microsoft.AspNetCore.JsonPatch.Exceptions;
Expand All @@ -15,14 +16,23 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

#if NET
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
#endif

namespace Microsoft.AspNetCore.JsonPatch;

// Implementation details: the purpose of this type of patch document is to ensure we can do type-checking
// when producing a JsonPatchDocument. However, we cannot send this "typed" over the wire, as that would require
// including type data in the JsonPatchDocument serialized as JSON (to allow for correct deserialization) - that's
// not according to RFC 6902, and would thus break cross-platform compatibility.
[JsonConverter(typeof(TypedJsonPatchDocumentConverter))]
#if NET
public class JsonPatchDocument<TModel> : IJsonPatchDocument, IEndpointParameterMetadataProvider where TModel : class
#else
public class JsonPatchDocument<TModel> : IJsonPatchDocument where TModel : class
#endif
{
public List<Operation<TModel>> Operations { get; private set; }

Expand Down Expand Up @@ -656,6 +666,23 @@ IList<Operation> IJsonPatchDocument.GetOperations()
return allOps;
}

#if NET
/// <summary>
/// Populates metadata for the related endpoint when this type is used as a parameter.
/// </summary>
/// <param name="parameter">The <see cref="ParameterInfo"/> for the endpoint parameter.</param>
/// <param name="builder">The endpoint builder for the endpoint being constructed.</param>
#pragma warning disable RS0016 // Add public types and members to the declared API
public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder)
#pragma warning restore RS0016 // Add public types and members to the declared API
{
ArgumentNullException.ThrowIfNull(parameter);
ArgumentNullException.ThrowIfNull(builder);

builder.Metadata.Add(new AcceptsMetadata(new[] { "application/json-patch+json" }, parameter.ParameterType));
}
#endif

// Internal for testing
internal string GetPath<TProp>(Expression<Func<TModel, TProp>> expr, string position)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<ItemGroup>
<Reference Include="Microsoft.CSharp" Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'" />
<Reference Include="Newtonsoft.Json" />
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp'" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions src/OpenApi/sample/Endpoints/MapSchemasEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Immutable;
using System.ComponentModel;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.JsonPatch.SystemTextJson;

public static class SchemasEndpointsExtensions
{
Expand Down Expand Up @@ -36,6 +37,7 @@ public static IEndpointRouteBuilder MapSchemasEndpoints(this IEndpointRouteBuild
schemas.MapPost("/location", (LocationContainer location) => { });
schemas.MapPost("/parent", (ParentObject parent) => Results.Ok(parent));
schemas.MapPost("/child", (ChildObject child) => Results.Ok(child));
schemas.MapPatch("/json-patch", (JsonPatchDocument<ParentObject> patchDoc) => Results.NoContent());

return endpointRouteBuilder;
}
Expand Down
1 change: 1 addition & 0 deletions src/OpenApi/sample/Sample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
<Reference Include="Microsoft.Extensions.FileProviders.Embedded" />
<Reference Include="Microsoft.AspNetCore.Mvc" />
<Reference Include="Microsoft.AspNetCore.JsonPatch.SystemTextJson" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,28 @@
}
}
}
},
"/schemas-by-ref/json-patch": {
"patch": {
"tags": [
"Sample"
],
"requestBody": {
"content": {
"application/json-patch+json": {
"schema": {
"$ref": "#/components/schemas/JsonPatchDocumentOfParentObject"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK"
}
}
}
}
},
"components": {
Expand Down Expand Up @@ -593,6 +615,7 @@
}
}
},
"JsonPatchDocumentOfParentObject": { },
"LocationContainer": {
"required": [
"location"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,28 @@
}
}
}
},
"/schemas-by-ref/json-patch": {
"patch": {
"tags": [
"Sample"
],
"requestBody": {
"content": {
"application/json-patch+json": {
"schema": {
"$ref": "#/components/schemas/JsonPatchDocumentOfParentObject"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK"
}
}
}
}
},
"components": {
Expand Down Expand Up @@ -593,6 +615,7 @@
}
}
},
"JsonPatchDocumentOfParentObject": { },
"LocationContainer": {
"required": [
"location"
Expand Down
Loading