Skip to content

Commit 369c4fd

Browse files
author
Bart Koelman
authored
Fixed: Paging links should not be rendered in POST response. (#880)
1 parent 15303c5 commit 369c4fd

File tree

3 files changed

+110
-1
lines changed

3 files changed

+110
-1
lines changed

src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ private static void SetupRequest(JsonApiRequest request, ResourceContext primary
183183
}
184184
}
185185

186-
request.IsCollection = request.PrimaryId == null || request.Relationship is HasManyAttribute;
186+
var isGetAll = request.PrimaryId == null && request.IsReadOnly;
187+
request.IsCollection = isGetAll || request.Relationship is HasManyAttribute;
187188
}
188189

189190
private static string GetPrimaryRequestId(RouteValueDictionary routeValues)

test/JsonApiDotNetCoreExampleTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public async Task Sets_location_header_for_created_resource()
5656
var newWorkItemId = responseDocument.SingleData.Id;
5757
httpResponse.Headers.Location.Should().Be("/workItems/" + newWorkItemId);
5858

59+
responseDocument.Links.Self.Should().Be("http://localhost/workItems");
60+
responseDocument.Links.First.Should().BeNull();
61+
5962
responseDocument.SingleData.Should().NotBeNull();
6063
responseDocument.SingleData.Links.Self.Should().Be("http://localhost" + httpResponse.Headers.Location);
6164
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using FluentAssertions;
5+
using JsonApiDotNetCore.Configuration;
6+
using JsonApiDotNetCore.Middleware;
7+
using JsonApiDotNetCoreExample.Models;
8+
using Microsoft.AspNetCore.Http;
9+
using Microsoft.AspNetCore.Http.Features;
10+
using Microsoft.Extensions.Logging.Abstractions;
11+
using Moq;
12+
using Xunit;
13+
14+
namespace UnitTests.Middleware
15+
{
16+
public sealed class JsonApiRequestTests
17+
{
18+
[Theory]
19+
[InlineData("GET", "/articles", true, EndpointKind.Primary, true)]
20+
[InlineData("GET", "/articles/1", false, EndpointKind.Primary, true)]
21+
[InlineData("GET", "/articles/1/author", false, EndpointKind.Secondary, true)]
22+
[InlineData("GET", "/articles/1/revisions", true, EndpointKind.Secondary, true)]
23+
[InlineData("GET", "/articles/1/relationships/author", false, EndpointKind.Relationship, true)]
24+
[InlineData("GET", "/articles/1/relationships/revisions", true, EndpointKind.Relationship, true)]
25+
[InlineData("POST", "/articles", false, EndpointKind.Primary, false)]
26+
[InlineData("POST", "/articles/1/relationships/revisions", true, EndpointKind.Relationship, false)]
27+
[InlineData("PATCH", "/articles/1", false, EndpointKind.Primary, false)]
28+
[InlineData("PATCH", "/articles/1/relationships/author", false, EndpointKind.Relationship, false)]
29+
[InlineData("PATCH", "/articles/1/relationships/revisions", true, EndpointKind.Relationship, false)]
30+
[InlineData("DELETE", "/articles/1", false, EndpointKind.Primary, false)]
31+
[InlineData("DELETE", "/articles/1/relationships/revisions", true, EndpointKind.Relationship, false)]
32+
public async Task Sets_request_properties_correctly(string requestMethod, string requestPath, bool expectIsCollection, EndpointKind expectKind, bool expectIsReadOnly)
33+
{
34+
// Arrange
35+
var options = new JsonApiOptions
36+
{
37+
UseRelativeLinks = true
38+
};
39+
40+
var graphBuilder = new ResourceGraphBuilder(options, NullLoggerFactory.Instance);
41+
graphBuilder.Add<Article>();
42+
graphBuilder.Add<Author>();
43+
graphBuilder.Add<Revision>();
44+
45+
var resourceGraph = graphBuilder.Build();
46+
47+
var controllerResourceMappingMock = new Mock<IControllerResourceMapping>();
48+
49+
controllerResourceMappingMock
50+
.Setup(x => x.GetAssociatedResource(It.IsAny<string>()))
51+
.Returns(typeof(Article));
52+
53+
var httpContext = new DefaultHttpContext();
54+
SetupRoutes(httpContext, requestMethod, requestPath);
55+
56+
var request = new JsonApiRequest();
57+
58+
var middleware = new JsonApiMiddleware(_ => Task.CompletedTask);
59+
60+
// Act
61+
await middleware.Invoke(httpContext, controllerResourceMappingMock.Object, options, request, resourceGraph);
62+
63+
// Assert
64+
request.IsCollection.Should().Be(expectIsCollection);
65+
request.Kind.Should().Be(expectKind);
66+
request.IsReadOnly.Should().Be(expectIsReadOnly);
67+
request.BasePath.Should().BeEmpty();
68+
request.PrimaryResource.Should().NotBeNull();
69+
request.PrimaryResource.PublicName.Should().Be("articles");
70+
}
71+
72+
private static void SetupRoutes(HttpContext httpContext, string requestMethod, string requestPath)
73+
{
74+
httpContext.Request.Method = requestMethod;
75+
76+
var feature = new RouteValuesFeature
77+
{
78+
RouteValues =
79+
{
80+
["controller"] = "theController",
81+
["action"] = "theAction"
82+
}
83+
};
84+
85+
var pathSegments = requestPath.Split("/", StringSplitOptions.RemoveEmptyEntries);
86+
if (pathSegments.Length > 1)
87+
{
88+
feature.RouteValues["id"] = pathSegments[1];
89+
90+
if (pathSegments.Length >= 3)
91+
{
92+
feature.RouteValues["relationshipName"] = pathSegments.Last();
93+
}
94+
}
95+
96+
if (pathSegments.Contains("relationships"))
97+
{
98+
feature.RouteValues["action"] = "Relationship";
99+
}
100+
101+
httpContext.Features.Set<IRouteValuesFeature>(feature);
102+
httpContext.SetEndpoint(new Endpoint(null, new EndpointMetadataCollection(), null));
103+
}
104+
}
105+
}

0 commit comments

Comments
 (0)