Skip to content

Commit 61e2915

Browse files
committed
return 404 if get by id returns null
1 parent 1718a11 commit 61e2915

File tree

3 files changed

+87
-31
lines changed

3 files changed

+87
-31
lines changed

src/JsonApiDotNetCore/Services/Operations/Processors/GetOpProcessor.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ private async Task<object> GetByIdAsync(Operation operation)
9090
{
9191
var id = TypeHelper.ConvertType<TId>(operation.Ref.Id);
9292
var result = await _getById.GetAsync(id);
93+
94+
// this is a bit ugly but we need to bomb the entire transaction if the entity cannot be found
95+
// in the future it would probably be better to return a result status along with the doc to
96+
// avoid throwing exceptions on 4xx errors.
97+
// consider response type (status, document)
98+
if (result == null)
99+
throw new JsonApiException(404, $"Could not find '{operation.Ref.Type}' record with id '{operation.Ref.Id}'");
100+
93101
var doc = _documentBuilder.GetData(
94102
_contextGraph.GetContextEntity(operation.GetResourceTypeName()),
95103
result);
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Threading.Tasks;
6+
using Bogus;
7+
using JsonApiDotNetCore.Internal;
8+
using JsonApiDotNetCore.Models.Operations;
9+
using JsonApiDotNetCoreExample.Data;
10+
using OperationsExampleTests.Factories;
11+
using Xunit;
12+
13+
namespace OperationsExampleTests
14+
{
15+
public class GetTests : Fixture, IDisposable
16+
{
17+
private readonly Faker _faker = new Faker();
18+
19+
[Fact]
20+
public async Task Can_Get_Author_By_Id()
21+
{
22+
// arrange
23+
var context = GetService<AppDbContext>();
24+
var author = AuthorFactory.Get();
25+
context.Authors.Add(author);
26+
context.SaveChanges();
27+
28+
var content = new
29+
{
30+
operations = new[] {
31+
new Dictionary<string, object> {
32+
{ "op", "get"},
33+
{ "ref", new { type = "authors", id = author.StringId } }
34+
}
35+
}
36+
};
37+
38+
// act
39+
var result = await PatchAsync<OperationsDocument>("api/bulk", content);
40+
41+
// assert
42+
Assert.NotNull(result.response);
43+
Assert.NotNull(result.data);
44+
Assert.Equal(HttpStatusCode.OK, result.response.StatusCode);
45+
Assert.Equal(1, result.data.Operations.Count);
46+
Assert.Equal(author.Id.ToString(), result.data.Operations.Single().DataObject.Id);
47+
}
48+
49+
[Fact]
50+
public async Task Get_Author_By_Id_Returns_404_If_NotFound()
51+
{
52+
// arrange
53+
var authorId = _faker.Random.Int(max: 0).ToString();
54+
55+
var content = new
56+
{
57+
operations = new[] {
58+
new Dictionary<string, object> {
59+
{ "op", "get"},
60+
{ "ref", new { type = "authors", id = authorId } }
61+
}
62+
}
63+
};
64+
65+
// act
66+
var (response, data) = await PatchAsync<ErrorCollection>("api/bulk", content);
67+
68+
// assert
69+
Assert.NotNull(response);
70+
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
71+
Assert.NotNull(data);
72+
Assert.Equal(1, data.Errors.Count);
73+
Assert.True(data.Errors[0].Detail.Contains("authors"), "The error detail should contain the name of the entity that could not be found.");
74+
Assert.True(data.Errors[0].Detail.Contains(authorId), "The error detail should contain the entity id that could not be found");
75+
Assert.True(data.Errors[0].Title.Contains("operation[0]"), "The error title should contain the operation identifier that failed");
76+
}
77+
}
78+
}

test/OperationsExampleTests/Get/GetTests.cs

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace OperationsExampleTests
1313
{
14-
public class GetTests : Fixture, IDisposable
14+
public class GetByIdTests : Fixture, IDisposable
1515
{
1616
private readonly Faker _faker = new Faker();
1717

@@ -46,36 +46,6 @@ public async Task Can_Get_Authors()
4646
Assert.Equal(HttpStatusCode.OK, result.response.StatusCode);
4747
Assert.Equal(1, result.data.Operations.Count);
4848
Assert.Equal(expectedCount, result.data.Operations.Single().DataList.Count);
49-
}
50-
51-
[Fact]
52-
public async Task Can_Get_Author_By_Id()
53-
{
54-
// arrange
55-
var context = GetService<AppDbContext>();
56-
var author = AuthorFactory.Get();
57-
context.Authors.Add(author);
58-
context.SaveChanges();
59-
60-
var content = new
61-
{
62-
operations = new[] {
63-
new Dictionary<string, object> {
64-
{ "op", "get"},
65-
{ "ref", new { type = "authors", id = author.StringId } }
66-
}
67-
}
68-
};
69-
70-
// act
71-
var result = await PatchAsync<OperationsDocument>("api/bulk", content);
72-
73-
// assert
74-
Assert.NotNull(result.response);
75-
Assert.NotNull(result.data);
76-
Assert.Equal(HttpStatusCode.OK, result.response.StatusCode);
77-
Assert.Equal(1, result.data.Operations.Count);
78-
Assert.Equal(author.Id.ToString(), result.data.Operations.Single().DataObject.Id);
7949
}
8050
}
8151
}

0 commit comments

Comments
 (0)