Skip to content

Commit 30e1c0d

Browse files
authoredApr 26, 2017
Merge pull request #96 from Gerfaut/patch-nullable-attributes
Allow PATCH objects with nullable attribute (e.g. DateTime?)
·
v5.7.12.2.0
2 parents d4cb99d + d89dd23 commit 30e1c0d

19 files changed

+350
-48
lines changed
 

‎src/JsonApiDotNetCore/Internal/TypeHelper.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Reflection;
32

43
namespace JsonApiDotNetCore.Internal
54
{
@@ -10,8 +9,7 @@ public static object ConvertType(object value, Type type)
109
if(value == null)
1110
return null;
1211

13-
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
14-
type = Nullable.GetUnderlyingType(type);
12+
type = Nullable.GetUnderlyingType(type) ?? type;
1513

1614
var stringValue = value.ToString();
1715

‎src/JsonApiDotNetCore/Models/AttrAttribute.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Reflection;
3+
using JsonApiDotNetCore.Internal;
34

45
namespace JsonApiDotNetCore.Models
56
{
@@ -26,10 +27,13 @@ public void SetValue(object entity, object newValue)
2627
var propertyInfo = entity
2728
.GetType()
2829
.GetProperty(InternalAttributeName);
29-
30-
var convertedValue = Convert.ChangeType(newValue, propertyInfo.PropertyType);
31-
32-
propertyInfo.SetValue(entity, convertedValue);
30+
31+
if (propertyInfo != null)
32+
{
33+
var convertedValue = TypeHelper.ConvertType(newValue, propertyInfo.PropertyType);
34+
35+
propertyInfo.SetValue(entity, convertedValue);
36+
}
3337
}
3438
}
3539
}

‎src/JsonApiDotNetCoreExample/Data/AppDbContext.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
using JsonApiDotNetCoreExample.Models;
12
using JsonApiDotNetCore.Models;
23
using JsonApiDotNetCoreExample.Models;
34
using Microsoft.EntityFrameworkCore;
5+
using System;
46

57
namespace JsonApiDotNetCoreExample.Data
68
{
@@ -12,6 +14,9 @@ public AppDbContext(DbContextOptions<AppDbContext> options)
1214

1315
protected override void OnModelCreating(ModelBuilder modelBuilder)
1416
{
17+
modelBuilder.Entity<TodoItem>()
18+
.Property(t => t.CreatedDate).HasDefaultValueSql("CURRENT_TIMESTAMP").IsRequired();
19+
1520
modelBuilder.Entity<TodoItem>()
1621
.HasOne(t => t.Assignee)
1722
.WithMany(p => p.AssignedTodoItems)

‎src/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.Designer.cs

Lines changed: 108 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.EntityFrameworkCore.Migrations;
4+
5+
namespace JsonApiDotNetCoreExample.Migrations
6+
{
7+
public partial class AddCreatesAndAchievedDates : Migration
8+
{
9+
protected override void Up(MigrationBuilder migrationBuilder)
10+
{
11+
migrationBuilder.AddColumn<DateTime>(
12+
name: "AchievedDate",
13+
table: "TodoItems",
14+
nullable: true);
15+
16+
migrationBuilder.AddColumn<DateTime>(
17+
name: "CreatedDate",
18+
table: "TodoItems",
19+
nullable: false,
20+
defaultValueSql: "CURRENT_TIMESTAMP");
21+
}
22+
23+
protected override void Down(MigrationBuilder migrationBuilder)
24+
{
25+
migrationBuilder.DropColumn(
26+
name: "AchievedDate",
27+
table: "TodoItems");
28+
29+
migrationBuilder.DropColumn(
30+
name: "CreatedDate",
31+
table: "TodoItems");
32+
}
33+
}
34+
}

‎src/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,16 @@ protected override void BuildModel(ModelBuilder modelBuilder)
3535
b.Property<int>("Id")
3636
.ValueGeneratedOnAdd();
3737

38+
b.Property<DateTime?>("AchievedDate");
39+
3840
b.Property<int?>("AssigneeId");
3941

4042
b.Property<Guid?>("CollectionId");
4143

44+
b.Property<DateTime>("CreatedDate")
45+
.ValueGeneratedOnAdd()
46+
.HasDefaultValueSql("CURRENT_TIMESTAMP");
47+
4248
b.Property<string>("Description");
4349

4450
b.Property<Guid>("GuidProperty");

‎src/JsonApiDotNetCoreExample/Models/TodoItem.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using JsonApiDotNetCore.Models;
33

44
namespace JsonApiDotNetCoreExample.Models
@@ -18,6 +18,12 @@ public TodoItem()
1818

1919
[Attr("guid-property")]
2020
public Guid GuidProperty { get; set; }
21+
22+
[Attr("created-date")]
23+
public DateTime CreatedDate { get; set; }
24+
25+
[Attr("achieved-date")]
26+
public DateTime? AchievedDate { get; set; }
2127

2228
public int? OwnerId { get; set; }
2329
public int? AssigneeId { get; set; }

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/AttributeFilterTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Net;
1+
using System.Net;
22
using System.Net.Http;
33
using System.Threading.Tasks;
44
using DotNetCoreDocs;
@@ -30,7 +30,8 @@ public AttributeFilterTests(DocsFixture<Startup, JsonDocWriter> fixture)
3030
_fixture = fixture;
3131
_todoItemFaker = new Faker<TodoItem>()
3232
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
33-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
33+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
34+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
3435

3536
_personFaker = new Faker<Person>()
3637
.RuleFor(p => p.FirstName, f => f.Name.FirstName())

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Net;
1+
using System.Net;
22
using System.Net.Http;
33
using System.Net.Http.Headers;
44
using System.Threading.Tasks;
@@ -35,7 +35,8 @@ public CreatingDataTests(DocsFixture<Startup, JsonDocWriter> fixture)
3535
_jsonApiContext = fixture.GetService<IJsonApiContext>();
3636
_todoItemFaker = new Faker<TodoItem>()
3737
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
38-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
38+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
39+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
3940
}
4041

4142
[Fact]
@@ -107,7 +108,8 @@ public async Task Cannot_Create_Entity_With_Client_Generate_Id()
107108
attributes = new
108109
{
109110
description = todoItem.Description,
110-
ordinal = todoItem.Ordinal
111+
ordinal = todoItem.Ordinal,
112+
createdDate = DateTime.Now
111113
}
112114
}
113115
};
@@ -145,7 +147,8 @@ public async Task Can_Create_Entity_With_Client_Defined_Id_If_Configured()
145147
attributes = new
146148
{
147149
description = todoItem.Description,
148-
ordinal = todoItem.Ordinal
150+
ordinal = todoItem.Ordinal,
151+
createdDate = DateTime.Now
149152
}
150153
}
151154
};
@@ -302,7 +305,8 @@ public async Task ShouldReceiveLocationHeader_InResponse()
302305
attributes = new
303306
{
304307
description = todoItem.Description,
305-
ordinal = todoItem.Ordinal
308+
ordinal = todoItem.Ordinal,
309+
createdDate = DateTime.Now
306310
}
307311
}
308312
};
@@ -339,7 +343,8 @@ public async Task Respond_409_ToIncorrectEntityType()
339343
attributes = new
340344
{
341345
description = todoItem.Description,
342-
ordinal = todoItem.Ordinal
346+
ordinal = todoItem.Ordinal,
347+
createdDate = DateTime.Now
343348
}
344349
}
345350
};

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeletingDataTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Linq;
1+
using System.Linq;
22
using System.Net;
33
using System.Net.Http;
44
using System.Threading.Tasks;
@@ -27,7 +27,8 @@ public DeletingDataTests(DocsFixture<Startup, JsonDocWriter> fixture)
2727
_context = fixture.GetService<AppDbContext>();
2828
_todoItemFaker = new Faker<TodoItem>()
2929
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
30-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
30+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
31+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
3132
}
3233

3334
[Fact]

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Included.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Net;
1+
using System.Net;
22
using System.Net.Http;
33
using System.Threading.Tasks;
44
using DotNetCoreDocs;
@@ -36,7 +36,8 @@ public Included(DocsFixture<Startup, JsonDocWriter> fixture)
3636

3737
_todoItemFaker = new Faker<TodoItem>()
3838
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
39-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
39+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
40+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
4041

4142
_todoItemCollectionFaker = new Faker<TodoItemCollection>()
4243
.RuleFor(t => t.Name, f => f.Company.CatchPhrase());

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/PagingTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Net;
1+
using System.Net;
22
using System.Net.Http;
33
using System.Threading.Tasks;
44
using DotNetCoreDocs;
@@ -36,7 +36,8 @@ public PagingTests(DocsFixture<Startup, JsonDocWriter> fixture)
3636

3737
_todoItemFaker = new Faker<TodoItem>()
3838
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
39-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
39+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
40+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
4041

4142
_todoItemCollectionFaker = new Faker<TodoItemCollection>()
4243
.RuleFor(t => t.Name, f => f.Company.CatchPhrase());

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Net;
1+
using System.Net;
22
using System.Net.Http;
33
using System.Threading.Tasks;
44
using DotNetCoreDocs;
@@ -29,7 +29,8 @@ public Relationships(DocsFixture<Startup, JsonDocWriter> fixture)
2929
_context = fixture.GetService<AppDbContext>();
3030
_todoItemFaker = new Faker<TodoItem>()
3131
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
32-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
32+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
33+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
3334
}
3435

3536
[Fact]

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using System.Net;
33
using System.Net.Http;
44
using System.Threading.Tasks;
@@ -33,7 +33,8 @@ public FetchingDataTests(DocsFixture<Startup, JsonDocWriter> fixture)
3333
_jsonApiContext = fixture.GetService<IJsonApiContext>();
3434
_todoItemFaker = new Faker<TodoItem>()
3535
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
36-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
36+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
37+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
3738
_personFaker = new Faker<Person>()
3839
.RuleFor(p => p.FirstName, f => f.Name.FirstName())
3940
.RuleFor(p => p.LastName, f => f.Name.LastName());

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingRelationshipsTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Linq;
1+
using System.Linq;
22
using System.Net;
33
using System.Net.Http;
44
using System.Threading.Tasks;
@@ -27,7 +27,8 @@ public FetchingRelationshipsTests(DocsFixture<Startup, JsonDocWriter> fixture)
2727
_jsonApiContext = fixture.GetService<IJsonApiContext>();
2828
_todoItemFaker = new Faker<TodoItem>()
2929
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
30-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
30+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
31+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
3132
}
3233

3334
[Fact]

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Threading.Tasks;
1+
using System.Threading.Tasks;
22
using DotNetCoreDocs;
33
using DotNetCoreDocs.Writers;
44
using JsonApiDotNetCoreExample;
@@ -33,14 +33,16 @@ public SparseFieldSetTests(DocsFixture<Startup, JsonDocWriter> fixture)
3333
public async Task Can_Select_Sparse_Fieldsets()
3434
{
3535
// arrange
36-
var fields = new string[] { "Id", "Description" };
36+
var fields = new string[] { "Id", "Description", "CreatedDate", "AchievedDate" };
3737
var todoItem = new TodoItem {
3838
Description = "description",
39-
Ordinal = 1
39+
Ordinal = 1,
40+
CreatedDate = System.DateTime.Now,
41+
AchievedDate = System.DateTime.Now.AddDays(2)
4042
};
4143
_dbContext.TodoItems.Add(todoItem);
4244
await _dbContext.SaveChangesAsync();
43-
var expectedSql = $@"SELECT 't'.'Id', 't'.'Description'
45+
var expectedSql = $@"SELECT 't'.'Id', 't'.'Description', 't'.'CreatedDate', 't'.'AchievedDate'
4446
FROM 'TodoItems' AS 't'
4547
WHERE 't'.'Id' = {todoItem.Id}".Normalize();
4648

@@ -56,6 +58,8 @@ public async Task Can_Select_Sparse_Fieldsets()
5658
// assert
5759
Assert.Equal(0, result.Ordinal);
5860
Assert.Equal(todoItem.Description, result.Description);
61+
Assert.Equal(todoItem.CreatedDate.ToString("G"), result.CreatedDate.ToString("G"));
62+
Assert.Equal(todoItem.AchievedDate.GetValueOrDefault().ToString("G"), result.AchievedDate.GetValueOrDefault().ToString("G"));
5963
Assert.Equal(expectedSql, resultSql);
6064
}
6165

@@ -65,7 +69,8 @@ public async Task Fields_Query_Selects_Sparse_Field_Sets()
6569
// arrange
6670
var todoItem = new TodoItem {
6771
Description = "description",
68-
Ordinal = 1
72+
Ordinal = 1,
73+
CreatedDate = System.DateTime.Now
6974
};
7075
_dbContext.TodoItems.Add(todoItem);
7176
await _dbContext.SaveChangesAsync();
@@ -76,7 +81,7 @@ public async Task Fields_Query_Selects_Sparse_Field_Sets()
7681
var server = new TestServer(builder);
7782
var client = server.CreateClient();
7883

79-
var route = $"/api/v1/todo-items/{todoItem.Id}?fields[todo-items]=description";
84+
var route = $"/api/v1/todo-items/{todoItem.Id}?fields[todo-items]=description,created-date";
8085
var request = new HttpRequestMessage(httpMethod, route);
8186

8287
// act
@@ -86,8 +91,9 @@ public async Task Fields_Query_Selects_Sparse_Field_Sets()
8691

8792
// assert
8893
Assert.Equal(todoItem.StringId, deserializeBody.Data.Id);
89-
Assert.Equal(1, deserializeBody.Data.Attributes.Count);
94+
Assert.Equal(2, deserializeBody.Data.Attributes.Count);
9095
Assert.Equal(todoItem.Description, deserializeBody.Data.Attributes["description"]);
96+
Assert.Equal(todoItem.CreatedDate, deserializeBody.Data.Attributes["created-date"]);
9197
}
9298
}
9399
}

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Linq;
23
using System.Net;
34
using System.Net.Http;
@@ -32,7 +33,8 @@ public UpdatingDataTests(DocsFixture<Startup, JsonDocWriter> fixture)
3233
_context = fixture.GetService<AppDbContext>();
3334
_todoItemFaker = new Faker<TodoItem>()
3435
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
35-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
36+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
37+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
3638
_personFaker = new Faker<Person>()
3739
.RuleFor(p => p.FirstName, f => f.Name.FirstName())
3840
.RuleFor(p => p.LastName, f => f.Name.LastName());
@@ -58,7 +60,8 @@ public async Task Respond_404_If_EntityDoesNotExist()
5860
attributes = new
5961
{
6062
description = todoItem.Description,
61-
ordinal = todoItem.Ordinal
63+
ordinal = todoItem.Ordinal,
64+
createdDate = DateTime.Now
6265
}
6366
}
6467
};
@@ -100,7 +103,8 @@ public async Task Can_Patch_Entity_And_HasOne_Relationships()
100103
attributes = new
101104
{
102105
description = todoItem.Description,
103-
ordinal = todoItem.Ordinal
106+
ordinal = todoItem.Ordinal,
107+
createdDate = DateTime.Now
104108
},
105109
relationships = new
106110
{

‎test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using System.Diagnostics;
33
using System.Linq;
44
using System.Net;
@@ -39,7 +39,8 @@ public UpdatingRelationshipsTests(DocsFixture<Startup, JsonDocWriter> fixture)
3939

4040
_todoItemFaker = new Faker<TodoItem>()
4141
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
42-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
42+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
43+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
4344
}
4445

4546
[Fact]

‎test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs

Lines changed: 126 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using System.Net;
33
using System.Net.Http;
44
using System.Net.Http.Headers;
@@ -34,7 +34,8 @@ public TodoItemControllerTests(DocsFixture<Startup, JsonDocWriter> fixture)
3434
_jsonApiContext = fixture.GetService<IJsonApiContext>();
3535
_todoItemFaker = new Faker<TodoItem>()
3636
.RuleFor(t => t.Description, f => f.Lorem.Sentence())
37-
.RuleFor(t => t.Ordinal, f => f.Random.Number());
37+
.RuleFor(t => t.Ordinal, f => f.Random.Number())
38+
.RuleFor(t => t.CreatedDate, f => f.Date.Past());
3839
}
3940

4041
[Fact]
@@ -265,6 +266,8 @@ public async Task Can_Get_TodoItem_ById()
265266
Assert.Equal(todoItem.Id, deserializedBody.Id);
266267
Assert.Equal(todoItem.Description, deserializedBody.Description);
267268
Assert.Equal(todoItem.Ordinal, deserializedBody.Ordinal);
269+
Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G"));
270+
Assert.Equal(null, deserializedBody.AchievedDate);
268271
}
269272

270273
[Fact]
@@ -296,6 +299,8 @@ public async Task Can_Get_TodoItem_WithOwner()
296299
Assert.Equal(todoItem.Id, deserializedBody.Id);
297300
Assert.Equal(todoItem.Description, deserializedBody.Description);
298301
Assert.Equal(todoItem.Ordinal, deserializedBody.Ordinal);
302+
Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G"));
303+
Assert.Equal(null, deserializedBody.AchievedDate);
299304
}
300305

301306
[Fact]
@@ -312,10 +317,11 @@ public async Task Can_Post_TodoItem()
312317
data = new
313318
{
314319
type = "todo-items",
315-
attributes = new
320+
attributes = new Dictionary<string, object>()
316321
{
317-
description = todoItem.Description,
318-
ordinal = todoItem.Ordinal
322+
{ "description", todoItem.Description },
323+
{ "ordinal", todoItem.Ordinal },
324+
{ "created-date", todoItem.CreatedDate }
319325
},
320326
relationships = new
321327
{
@@ -348,6 +354,8 @@ public async Task Can_Post_TodoItem()
348354
// Assert
349355
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
350356
Assert.Equal(todoItem.Description, deserializedBody.Description);
357+
Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G"));
358+
Assert.Equal(null, deserializedBody.AchievedDate);
351359
}
352360

353361
[Fact]
@@ -370,10 +378,11 @@ public async Task Can_Patch_TodoItem()
370378
data = new
371379
{
372380
type = "todo-items",
373-
attributes = new
381+
attributes = new Dictionary<string, object>()
374382
{
375-
description = newTodoItem.Description,
376-
ordinal = newTodoItem.Ordinal
383+
{ "description", newTodoItem.Description },
384+
{ "ordinal", newTodoItem.Ordinal },
385+
{ "created-date", newTodoItem.CreatedDate }
377386
}
378387
}
379388
};
@@ -396,6 +405,115 @@ public async Task Can_Patch_TodoItem()
396405
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
397406
Assert.Equal(newTodoItem.Description, deserializedBody.Description);
398407
Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal);
408+
Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G"));
409+
Assert.Equal(null, deserializedBody.AchievedDate);
410+
}
411+
412+
[Fact]
413+
public async Task Can_Patch_TodoItemWithNullable()
414+
{
415+
// Arrange
416+
var person = new Person();
417+
_context.People.Add(person);
418+
_context.SaveChanges();
419+
420+
var todoItem = _todoItemFaker.Generate();
421+
todoItem.AchievedDate = System.DateTime.Now;
422+
todoItem.Owner = person;
423+
_context.TodoItems.Add(todoItem);
424+
_context.SaveChanges();
425+
426+
var newTodoItem = _todoItemFaker.Generate();
427+
newTodoItem.AchievedDate = System.DateTime.Now.AddDays(2);
428+
429+
var content = new
430+
{
431+
data = new
432+
{
433+
type = "todo-items",
434+
attributes = new Dictionary<string, object>()
435+
{
436+
{ "description", newTodoItem.Description },
437+
{ "ordinal", newTodoItem.Ordinal },
438+
{ "created-date", newTodoItem.CreatedDate },
439+
{ "achieved-date", newTodoItem.AchievedDate }
440+
}
441+
}
442+
};
443+
444+
var httpMethod = new HttpMethod("PATCH");
445+
var route = $"/api/v1/todo-items/{todoItem.Id}";
446+
447+
var request = new HttpRequestMessage(httpMethod, route);
448+
request.Content = new StringContent(JsonConvert.SerializeObject(content));
449+
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
450+
451+
var description = new RequestProperties("Patch TodoItem");
452+
453+
// Act
454+
var response = await _fixture.MakeRequest<TodoItem>(description, request);
455+
var body = await response.Content.ReadAsStringAsync();
456+
var deserializedBody = (TodoItem)_fixture.GetService<IJsonApiDeSerializer>().Deserialize(body);
457+
458+
// Assert
459+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
460+
Assert.Equal(newTodoItem.Description, deserializedBody.Description);
461+
Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal);
462+
Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G"));
463+
Assert.Equal(newTodoItem.AchievedDate.GetValueOrDefault().ToString("G"), deserializedBody.AchievedDate.GetValueOrDefault().ToString("G"));
464+
}
465+
466+
[Fact]
467+
public async Task Can_Patch_TodoItemWithNullValue()
468+
{
469+
// Arrange
470+
var person = new Person();
471+
_context.People.Add(person);
472+
_context.SaveChanges();
473+
474+
var todoItem = _todoItemFaker.Generate();
475+
todoItem.AchievedDate = System.DateTime.Now;
476+
todoItem.Owner = person;
477+
_context.TodoItems.Add(todoItem);
478+
_context.SaveChanges();
479+
480+
var newTodoItem = _todoItemFaker.Generate();
481+
482+
var content = new
483+
{
484+
data = new
485+
{
486+
type = "todo-items",
487+
attributes = new Dictionary<string, object>()
488+
{
489+
{ "description", newTodoItem.Description },
490+
{ "ordinal", newTodoItem.Ordinal },
491+
{ "created-date", newTodoItem.CreatedDate },
492+
{ "achieved-date", null }
493+
}
494+
}
495+
};
496+
497+
var httpMethod = new HttpMethod("PATCH");
498+
var route = $"/api/v1/todo-items/{todoItem.Id}";
499+
500+
var request = new HttpRequestMessage(httpMethod, route);
501+
request.Content = new StringContent(JsonConvert.SerializeObject(content));
502+
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json");
503+
504+
var description = new RequestProperties("Patch TodoItem");
505+
506+
// Act
507+
var response = await _fixture.MakeRequest<TodoItem>(description, request);
508+
var body = await response.Content.ReadAsStringAsync();
509+
var deserializedBody = (TodoItem)_fixture.GetService<IJsonApiDeSerializer>().Deserialize(body);
510+
511+
// Assert
512+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
513+
Assert.Equal(newTodoItem.Description, deserializedBody.Description);
514+
Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal);
515+
Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G"));
516+
Assert.Equal(null, deserializedBody.AchievedDate);
399517
}
400518

401519
[Fact]

0 commit comments

Comments
 (0)
Please sign in to comment.