Skip to content

Commit 30e1c0d

Browse files
authored
Merge pull request #96 from Gerfaut/patch-nullable-attributes
Allow PATCH objects with nullable attribute (e.g. DateTime?)
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());

0 commit comments

Comments
 (0)