Skip to content

Commit b1ecb9a

Browse files
author
Christophe Leemans
committed
Allow PATCH objects with nullable attribute (e.g. DateTime?)
1 parent 219ccfc commit b1ecb9a

18 files changed

+354
-50
lines changed

src/JsonApiDotNetCore/Models/AttrAttribute.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,15 @@ public void SetValue(object entity, object newValue)
2626
var propertyInfo = entity
2727
.GetType()
2828
.GetProperty(InternalAttributeName);
29-
30-
var convertedValue = Convert.ChangeType(newValue, propertyInfo.PropertyType);
31-
32-
propertyInfo.SetValue(entity, convertedValue);
29+
30+
if (propertyInfo != null)
31+
{
32+
Type t = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
33+
34+
var convertedValue = (newValue == null) ? null : Convert.ChangeType(newValue, t);
35+
36+
propertyInfo.SetValue(entity, convertedValue, null);
37+
}
3338
}
3439
}
3540
}

src/JsonApiDotNetCoreExample/Data/AppDbContext.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
using JsonApiDotNetCoreExample.Models;
1+
using JsonApiDotNetCoreExample.Models;
22
using Microsoft.EntityFrameworkCore;
3+
using System;
34

45
namespace JsonApiDotNetCoreExample.Data
56
{
@@ -11,7 +12,10 @@ public AppDbContext(DbContextOptions<AppDbContext> options)
1112

1213
protected override void OnModelCreating(ModelBuilder modelBuilder)
1314
{
14-
modelBuilder.Entity<TodoItem>()
15+
modelBuilder.Entity<TodoItem>()
16+
.Property(t => t.CreatedDate).HasDefaultValueSql("CURRENT_TIMESTAMP").IsRequired();
17+
18+
modelBuilder.Entity<TodoItem>()
1519
.HasOne(t => t.Assignee)
1620
.WithMany(p => p.AssignedTodoItems)
1721
.HasForeignKey(t => t.AssigneeId);

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());

0 commit comments

Comments
 (0)