From b1ecb9a567b64b9990a6a147e6534370bcc27c4e Mon Sep 17 00:00:00 2001 From: Christophe Leemans Date: Tue, 25 Apr 2017 23:54:11 +0200 Subject: [PATCH 1/4] Allow PATCH objects with nullable attribute (e.g. DateTime?) --- src/JsonApiDotNetCore/Models/AttrAttribute.cs | 13 +- .../Data/AppDbContext.cs | 8 +- ...950_AddCreatesAndAchievedDates.Designer.cs | 108 ++++++++++++++ ...170424180950_AddCreatesAndAchievedDates.cs | 34 +++++ .../Migrations/AppDbContextModelSnapshot.cs | 6 + .../Models/TodoItem.cs | 8 +- .../Acceptance/Spec/AttributeFilterTests.cs | 5 +- .../Acceptance/Spec/CreatingDataTests.cs | 17 ++- .../Acceptance/Spec/DeletingDataTests.cs | 5 +- .../Acceptance/Spec/DocumentTests/Included.cs | 5 +- .../Spec/DocumentTests/PagingTests.cs | 5 +- .../Spec/DocumentTests/Relationships.cs | 5 +- .../Acceptance/Spec/FetchingDataTests.cs | 5 +- .../Spec/FetchingRelationshipsTests.cs | 5 +- .../Acceptance/Spec/SparseFieldSetTests.cs | 20 ++- .../Acceptance/Spec/UpdatingDataTests.cs | 10 +- .../Spec/UpdatingRelationshipsTests.cs | 5 +- .../Acceptance/TodoItemsControllerTests.cs | 140 ++++++++++++++++-- 18 files changed, 354 insertions(+), 50 deletions(-) create mode 100755 src/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.Designer.cs create mode 100755 src/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.cs diff --git a/src/JsonApiDotNetCore/Models/AttrAttribute.cs b/src/JsonApiDotNetCore/Models/AttrAttribute.cs index 3ce9a2196a..7926bcfd97 100644 --- a/src/JsonApiDotNetCore/Models/AttrAttribute.cs +++ b/src/JsonApiDotNetCore/Models/AttrAttribute.cs @@ -26,10 +26,15 @@ public void SetValue(object entity, object newValue) var propertyInfo = entity .GetType() .GetProperty(InternalAttributeName); - - var convertedValue = Convert.ChangeType(newValue, propertyInfo.PropertyType); - - propertyInfo.SetValue(entity, convertedValue); + + if (propertyInfo != null) + { + Type t = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType; + + var convertedValue = (newValue == null) ? null : Convert.ChangeType(newValue, t); + + propertyInfo.SetValue(entity, convertedValue, null); + } } } } diff --git a/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 592513b94d..d7f827b1a0 100644 --- a/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -1,5 +1,6 @@ -using JsonApiDotNetCoreExample.Models; +using JsonApiDotNetCoreExample.Models; using Microsoft.EntityFrameworkCore; +using System; namespace JsonApiDotNetCoreExample.Data { @@ -11,7 +12,10 @@ public AppDbContext(DbContextOptions options) protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity() + modelBuilder.Entity() + .Property(t => t.CreatedDate).HasDefaultValueSql("CURRENT_TIMESTAMP").IsRequired(); + + modelBuilder.Entity() .HasOne(t => t.Assignee) .WithMany(p => p.AssignedTodoItems) .HasForeignKey(t => t.AssigneeId); diff --git a/src/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.Designer.cs b/src/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.Designer.cs new file mode 100755 index 0000000000..537b3b043a --- /dev/null +++ b/src/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.Designer.cs @@ -0,0 +1,108 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using JsonApiDotNetCoreExample.Data; + +namespace JsonApiDotNetCoreExample.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20170424180950_AddCreatesAndAchievedDates")] + partial class AddCreatesAndAchievedDates + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) + .HasAnnotation("ProductVersion", "1.1.1"); + + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("FirstName"); + + b.Property("LastName"); + + b.HasKey("Id"); + + b.ToTable("People"); + }); + + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AchievedDate"); + + b.Property("AssigneeId"); + + b.Property("CollectionId"); + + b.Property("CreatedDate") + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Description"); + + b.Property("GuidProperty"); + + b.Property("Ordinal"); + + b.Property("OwnerId"); + + b.HasKey("Id"); + + b.HasIndex("AssigneeId"); + + b.HasIndex("CollectionId"); + + b.HasIndex("OwnerId"); + + b.ToTable("TodoItems"); + }); + + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name"); + + b.Property("OwnerId"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("TodoItemCollections"); + }); + + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b => + { + b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Assignee") + .WithMany("AssignedTodoItems") + .HasForeignKey("AssigneeId"); + + b.HasOne("JsonApiDotNetCoreExample.Models.TodoItemCollection", "Collection") + .WithMany("TodoItems") + .HasForeignKey("CollectionId"); + + b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner") + .WithMany("TodoItems") + .HasForeignKey("OwnerId"); + }); + + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b => + { + b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner") + .WithMany("TodoItemCollections") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.cs b/src/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.cs new file mode 100755 index 0000000000..e1c04cf961 --- /dev/null +++ b/src/JsonApiDotNetCoreExample/Migrations/20170424180950_AddCreatesAndAchievedDates.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace JsonApiDotNetCoreExample.Migrations +{ + public partial class AddCreatesAndAchievedDates : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AchievedDate", + table: "TodoItems", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedDate", + table: "TodoItems", + nullable: false, + defaultValueSql: "CURRENT_TIMESTAMP"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AchievedDate", + table: "TodoItems"); + + migrationBuilder.DropColumn( + name: "CreatedDate", + table: "TodoItems"); + } + } +} diff --git a/src/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs b/src/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs index 6912fa093a..f662ace4cc 100755 --- a/src/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs +++ b/src/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs @@ -35,10 +35,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("AchievedDate"); + b.Property("AssigneeId"); b.Property("CollectionId"); + b.Property("CreatedDate") + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + b.Property("Description"); b.Property("GuidProperty"); diff --git a/src/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/JsonApiDotNetCoreExample/Models/TodoItem.cs index 27ad9716c6..cd3ab4be4d 100644 --- a/src/JsonApiDotNetCoreExample/Models/TodoItem.cs +++ b/src/JsonApiDotNetCoreExample/Models/TodoItem.cs @@ -1,4 +1,4 @@ -using System; +using System; using JsonApiDotNetCore.Models; namespace JsonApiDotNetCoreExample.Models @@ -18,6 +18,12 @@ public TodoItem() [Attr("guid-property")] public Guid GuidProperty { get; set; } + + [Attr("created-date")] + public DateTime CreatedDate { get; set; } + + [Attr("achieved-date")] + public DateTime? AchievedDate { get; set; } public int? OwnerId { get; set; } public int? AssigneeId { get; set; } diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/AttributeFilterTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/AttributeFilterTests.cs index 3405eab431..9f2c369d28 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/AttributeFilterTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/AttributeFilterTests.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using DotNetCoreDocs; @@ -30,7 +30,8 @@ public AttributeFilterTests(DocsFixture fixture) _fixture = fixture; _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); _personFaker = new Faker() .RuleFor(p => p.FirstName, f => f.Name.FirstName()) diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs index 52b399191f..09e7d84817 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; @@ -35,7 +35,8 @@ public CreatingDataTests(DocsFixture fixture) _jsonApiContext = fixture.GetService(); _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] @@ -107,7 +108,8 @@ public async Task Cannot_Create_Entity_With_Client_Generate_Id() attributes = new { description = todoItem.Description, - ordinal = todoItem.Ordinal + ordinal = todoItem.Ordinal, + createdDate = DateTime.Now } } }; @@ -145,7 +147,8 @@ public async Task Can_Create_Entity_With_Client_Defined_Id_If_Configured() attributes = new { description = todoItem.Description, - ordinal = todoItem.Ordinal + ordinal = todoItem.Ordinal, + createdDate = DateTime.Now } } }; @@ -302,7 +305,8 @@ public async Task ShouldReceiveLocationHeader_InResponse() attributes = new { description = todoItem.Description, - ordinal = todoItem.Ordinal + ordinal = todoItem.Ordinal, + createdDate = DateTime.Now } } }; @@ -339,7 +343,8 @@ public async Task Respond_409_ToIncorrectEntityType() attributes = new { description = todoItem.Description, - ordinal = todoItem.Ordinal + ordinal = todoItem.Ordinal, + createdDate = DateTime.Now } } }; diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeletingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeletingDataTests.cs index 6b30e43a0e..d609f21fe8 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeletingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeletingDataTests.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -27,7 +27,8 @@ public DeletingDataTests(DocsFixture fixture) _context = fixture.GetService(); _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Included.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Included.cs index 39747fd68e..ba1e05af5b 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Included.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Included.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using DotNetCoreDocs; @@ -36,7 +36,8 @@ public Included(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); _todoItemCollectionFaker = new Faker() .RuleFor(t => t.Name, f => f.Company.CatchPhrase()); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/PagingTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/PagingTests.cs index 0ea3b5a0d2..47b0ec6b3b 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/PagingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/PagingTests.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using DotNetCoreDocs; @@ -36,7 +36,8 @@ public PagingTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); _todoItemCollectionFaker = new Faker() .RuleFor(t => t.Name, f => f.Company.CatchPhrase()); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs index 744a395ce5..b978c89ead 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using DotNetCoreDocs; @@ -29,7 +29,8 @@ public Relationships(DocsFixture fixture) _context = fixture.GetService(); _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs index 7ff0b05e85..25654b7efb 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -33,7 +33,8 @@ public FetchingDataTests(DocsFixture fixture) _jsonApiContext = fixture.GetService(); _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); _personFaker = new Faker() .RuleFor(p => p.FirstName, f => f.Name.FirstName()) .RuleFor(p => p.LastName, f => f.Name.LastName()); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingRelationshipsTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingRelationshipsTests.cs index e6ca2663c0..dedb0cf59f 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingRelationshipsTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingRelationshipsTests.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -27,7 +27,8 @@ public FetchingRelationshipsTests(DocsFixture fixture) _jsonApiContext = fixture.GetService(); _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs index 2b0be2dc59..38f41124aa 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DotNetCoreDocs; using DotNetCoreDocs.Writers; using JsonApiDotNetCoreExample; @@ -33,14 +33,16 @@ public SparseFieldSetTests(DocsFixture fixture) public async Task Can_Select_Sparse_Fieldsets() { // arrange - var fields = new string[] { "Id", "Description" }; + var fields = new string[] { "Id", "Description", "CreatedDate", "AchievedDate" }; var todoItem = new TodoItem { Description = "description", - Ordinal = 1 + Ordinal = 1, + CreatedDate = System.DateTime.Now, + AchievedDate = System.DateTime.Now.AddDays(2) }; _dbContext.TodoItems.Add(todoItem); await _dbContext.SaveChangesAsync(); - var expectedSql = $@"SELECT 't'.'Id', 't'.'Description' + var expectedSql = $@"SELECT 't'.'Id', 't'.'Description', 't'.'CreatedDate', 't'.'AchievedDate' FROM 'TodoItems' AS 't' WHERE 't'.'Id' = {todoItem.Id}".Normalize(); @@ -56,6 +58,8 @@ public async Task Can_Select_Sparse_Fieldsets() // assert Assert.Equal(0, result.Ordinal); Assert.Equal(todoItem.Description, result.Description); + Assert.Equal(todoItem.CreatedDate.ToString("G"), result.CreatedDate.ToString("G")); + Assert.Equal(todoItem.AchievedDate.GetValueOrDefault().ToString("G"), result.AchievedDate.GetValueOrDefault().ToString("G")); Assert.Equal(expectedSql, resultSql); } @@ -65,7 +69,8 @@ public async Task Fields_Query_Selects_Sparse_Field_Sets() // arrange var todoItem = new TodoItem { Description = "description", - Ordinal = 1 + Ordinal = 1, + CreatedDate = System.DateTime.Now }; _dbContext.TodoItems.Add(todoItem); await _dbContext.SaveChangesAsync(); @@ -76,7 +81,7 @@ public async Task Fields_Query_Selects_Sparse_Field_Sets() var server = new TestServer(builder); var client = server.CreateClient(); - var route = $"/api/v1/todo-items/{todoItem.Id}?fields[todo-items]=description"; + var route = $"/api/v1/todo-items/{todoItem.Id}?fields[todo-items]=description,created-date"; var request = new HttpRequestMessage(httpMethod, route); // act @@ -86,8 +91,9 @@ public async Task Fields_Query_Selects_Sparse_Field_Sets() // assert Assert.Equal(todoItem.StringId, deserializeBody.Data.Id); - Assert.Equal(1, deserializeBody.Data.Attributes.Count); + Assert.Equal(2, deserializeBody.Data.Attributes.Count); Assert.Equal(todoItem.Description, deserializeBody.Data.Attributes["description"]); + Assert.Equal(todoItem.CreatedDate, deserializeBody.Data.Attributes["created-date"]); } } } diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs index a5ad8f6a7a..2377389d7b 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Net; using System.Net.Http; @@ -32,7 +33,8 @@ public UpdatingDataTests(DocsFixture fixture) _context = fixture.GetService(); _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); _personFaker = new Faker() .RuleFor(p => p.FirstName, f => f.Name.FirstName()) .RuleFor(p => p.LastName, f => f.Name.LastName()); @@ -58,7 +60,8 @@ public async Task Respond_404_If_EntityDoesNotExist() attributes = new { description = todoItem.Description, - ordinal = todoItem.Ordinal + ordinal = todoItem.Ordinal, + createdDate = DateTime.Now } } }; @@ -100,7 +103,8 @@ public async Task Can_Patch_Entity_And_HasOne_Relationships() attributes = new { description = todoItem.Description, - ordinal = todoItem.Ordinal + ordinal = todoItem.Ordinal, + createdDate = DateTime.Now }, relationships = new { diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs index ffb336e2c5..18b2ba214d 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; @@ -39,7 +39,8 @@ public UpdatingRelationshipsTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs index 84e398d4b9..7b8fbe9b62 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -34,7 +34,8 @@ public TodoItemControllerTests(DocsFixture fixture) _jsonApiContext = fixture.GetService(); _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) - .RuleFor(t => t.Ordinal, f => f.Random.Number()); + .RuleFor(t => t.Ordinal, f => f.Random.Number()) + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] @@ -265,6 +266,8 @@ public async Task Can_Get_TodoItem_ById() Assert.Equal(todoItem.Id, deserializedBody.Id); Assert.Equal(todoItem.Description, deserializedBody.Description); Assert.Equal(todoItem.Ordinal, deserializedBody.Ordinal); + Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(null, deserializedBody.AchievedDate); } [Fact] @@ -296,6 +299,8 @@ public async Task Can_Get_TodoItem_WithOwner() Assert.Equal(todoItem.Id, deserializedBody.Id); Assert.Equal(todoItem.Description, deserializedBody.Description); Assert.Equal(todoItem.Ordinal, deserializedBody.Ordinal); + Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(null, deserializedBody.AchievedDate); } [Fact] @@ -312,10 +317,11 @@ public async Task Can_Post_TodoItem() data = new { type = "todo-items", - attributes = new + attributes = new Dictionary() { - description = todoItem.Description, - ordinal = todoItem.Ordinal + { "description", todoItem.Description }, + { "ordinal", todoItem.Ordinal }, + { "created-date", todoItem.CreatedDate } }, relationships = new { @@ -348,6 +354,8 @@ public async Task Can_Post_TodoItem() // Assert Assert.Equal(HttpStatusCode.Created, response.StatusCode); Assert.Equal(todoItem.Description, deserializedBody.Description); + Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(null, deserializedBody.AchievedDate); } [Fact] @@ -370,12 +378,13 @@ public async Task Can_Patch_TodoItem() data = new { type = "todo-items", - attributes = new - { - description = newTodoItem.Description, - ordinal = newTodoItem.Ordinal - } - } + attributes = new Dictionary() + { + { "description", newTodoItem.Description }, + { "ordinal", newTodoItem.Ordinal }, + { "created-date", newTodoItem.CreatedDate } + } + } }; var httpMethod = new HttpMethod("PATCH"); @@ -396,8 +405,117 @@ public async Task Can_Patch_TodoItem() Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(newTodoItem.Description, deserializedBody.Description); Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal); + Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(null, deserializedBody.AchievedDate); } + [Fact] + public async Task Can_Patch_TodoItemWithNullable() + { + // Arrange + var person = new Person(); + _context.People.Add(person); + _context.SaveChanges(); + + var todoItem = _todoItemFaker.Generate(); + todoItem.AchievedDate = System.DateTime.Now; + todoItem.Owner = person; + _context.TodoItems.Add(todoItem); + _context.SaveChanges(); + + var newTodoItem = _todoItemFaker.Generate(); + newTodoItem.AchievedDate = System.DateTime.Now.AddDays(2); + + var content = new + { + data = new + { + type = "todo-items", + attributes = new Dictionary() + { + { "description", newTodoItem.Description }, + { "ordinal", newTodoItem.Ordinal }, + { "created-date", newTodoItem.CreatedDate }, + { "achieved-date", newTodoItem.AchievedDate } + } + } + }; + + var httpMethod = new HttpMethod("PATCH"); + var route = $"/api/v1/todo-items/{todoItem.Id}"; + + var request = new HttpRequestMessage(httpMethod, route); + request.Content = new StringContent(JsonConvert.SerializeObject(content)); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json"); + + var description = new RequestProperties("Patch TodoItem"); + + // Act + var response = await _fixture.MakeRequest(description, request); + var body = await response.Content.ReadAsStringAsync(); + var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(newTodoItem.Description, deserializedBody.Description); + Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal); + Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(newTodoItem.AchievedDate.GetValueOrDefault().ToString("G"), deserializedBody.AchievedDate.GetValueOrDefault().ToString("G")); + } + + [Fact] + public async Task Can_Patch_TodoItemWithNullValue() + { + // Arrange + var person = new Person(); + _context.People.Add(person); + _context.SaveChanges(); + + var todoItem = _todoItemFaker.Generate(); + todoItem.AchievedDate = System.DateTime.Now; + todoItem.Owner = person; + _context.TodoItems.Add(todoItem); + _context.SaveChanges(); + + var newTodoItem = _todoItemFaker.Generate(); + + var content = new + { + data = new + { + type = "todo-items", + attributes = new Dictionary() + { + { "description", newTodoItem.Description }, + { "ordinal", newTodoItem.Ordinal }, + { "created-date", newTodoItem.CreatedDate }, + { "achieved-date", null } + } + } + }; + + var httpMethod = new HttpMethod("PATCH"); + var route = $"/api/v1/todo-items/{todoItem.Id}"; + + var request = new HttpRequestMessage(httpMethod, route); + request.Content = new StringContent(JsonConvert.SerializeObject(content)); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json"); + + var description = new RequestProperties("Patch TodoItem"); + + // Act + var response = await _fixture.MakeRequest(description, request); + var body = await response.Content.ReadAsStringAsync(); + var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(newTodoItem.Description, deserializedBody.Description); + Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal); + Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(null, deserializedBody.AchievedDate); + } + [Fact] public async Task Can_Delete_TodoItem() { From 38f48181f6be152107ac0f7e8c6d7aefc8fa0db1 Mon Sep 17 00:00:00 2001 From: Christophe Leemans Date: Wed, 26 Apr 2017 21:30:50 +0200 Subject: [PATCH 2/4] refactor(serialization): use TypeHelper.ConvertType Use TypeHelper.ConvertType to keep consistency. --- src/JsonApiDotNetCore/Models/AttrAttribute.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/JsonApiDotNetCore/Models/AttrAttribute.cs b/src/JsonApiDotNetCore/Models/AttrAttribute.cs index 7926bcfd97..f9b9f89d89 100644 --- a/src/JsonApiDotNetCore/Models/AttrAttribute.cs +++ b/src/JsonApiDotNetCore/Models/AttrAttribute.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using JsonApiDotNetCore.Internal; namespace JsonApiDotNetCore.Models { @@ -27,14 +28,12 @@ public void SetValue(object entity, object newValue) .GetType() .GetProperty(InternalAttributeName); - if (propertyInfo != null) - { - Type t = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType; + if (propertyInfo != null) + { + var convertedValue = TypeHelper.ConvertType(newValue, propertyInfo.PropertyType); - var convertedValue = (newValue == null) ? null : Convert.ChangeType(newValue, t); - - propertyInfo.SetValue(entity, convertedValue, null); - } + propertyInfo.SetValue(entity, convertedValue); + } } } } From aeb41af46cb2eea7170728857f5e79451323a4be Mon Sep 17 00:00:00 2001 From: Christophe Leemans Date: Wed, 26 Apr 2017 21:31:30 +0200 Subject: [PATCH 3/4] fix(format): convert tabs to spaces Fix use spaces instead of tabs. --- .../Data/AppDbContext.cs | 8 +- .../Models/TodoItem.cs | 8 +- .../Acceptance/Spec/AttributeFilterTests.cs | 2 +- .../Acceptance/Spec/CreatingDataTests.cs | 10 +- .../Acceptance/Spec/DeletingDataTests.cs | 2 +- .../Acceptance/Spec/DocumentTests/Included.cs | 2 +- .../Spec/DocumentTests/PagingTests.cs | 2 +- .../Spec/DocumentTests/Relationships.cs | 2 +- .../Acceptance/Spec/FetchingDataTests.cs | 2 +- .../Spec/FetchingRelationshipsTests.cs | 2 +- .../Acceptance/Spec/SparseFieldSetTests.cs | 12 +- .../Acceptance/Spec/UpdatingDataTests.cs | 8 +- .../Spec/UpdatingRelationshipsTests.cs | 2 +- .../Acceptance/TodoItemsControllerTests.cs | 248 +++++++++--------- 14 files changed, 155 insertions(+), 155 deletions(-) diff --git a/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs index dda43c53b8..a54c2a93e8 100644 --- a/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -14,10 +14,10 @@ public AppDbContext(DbContextOptions options) protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity() - .Property(t => t.CreatedDate).HasDefaultValueSql("CURRENT_TIMESTAMP").IsRequired(); - - modelBuilder.Entity() + modelBuilder.Entity() + .Property(t => t.CreatedDate).HasDefaultValueSql("CURRENT_TIMESTAMP").IsRequired(); + + modelBuilder.Entity() .HasOne(t => t.Assignee) .WithMany(p => p.AssignedTodoItems) .HasForeignKey(t => t.AssigneeId); diff --git a/src/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/JsonApiDotNetCoreExample/Models/TodoItem.cs index cd3ab4be4d..7257835791 100644 --- a/src/JsonApiDotNetCoreExample/Models/TodoItem.cs +++ b/src/JsonApiDotNetCoreExample/Models/TodoItem.cs @@ -19,11 +19,11 @@ public TodoItem() [Attr("guid-property")] public Guid GuidProperty { get; set; } - [Attr("created-date")] - public DateTime CreatedDate { get; set; } + [Attr("created-date")] + public DateTime CreatedDate { get; set; } - [Attr("achieved-date")] - public DateTime? AchievedDate { get; set; } + [Attr("achieved-date")] + public DateTime? AchievedDate { get; set; } public int? OwnerId { get; set; } public int? AssigneeId { get; set; } diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/AttributeFilterTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/AttributeFilterTests.cs index 9f2c369d28..42c3f0b1f1 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/AttributeFilterTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/AttributeFilterTests.cs @@ -31,7 +31,7 @@ public AttributeFilterTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); _personFaker = new Faker() .RuleFor(p => p.FirstName, f => f.Name.FirstName()) diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs index 3850623a1e..08db60d635 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs @@ -36,7 +36,7 @@ public CreatingDataTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] @@ -109,7 +109,7 @@ public async Task Cannot_Create_Entity_With_Client_Generate_Id() { description = todoItem.Description, ordinal = todoItem.Ordinal, - createdDate = DateTime.Now + createdDate = DateTime.Now } } }; @@ -148,7 +148,7 @@ public async Task Can_Create_Entity_With_Client_Defined_Id_If_Configured() { description = todoItem.Description, ordinal = todoItem.Ordinal, - createdDate = DateTime.Now + createdDate = DateTime.Now } } }; @@ -306,7 +306,7 @@ public async Task ShouldReceiveLocationHeader_InResponse() { description = todoItem.Description, ordinal = todoItem.Ordinal, - createdDate = DateTime.Now + createdDate = DateTime.Now } } }; @@ -344,7 +344,7 @@ public async Task Respond_409_ToIncorrectEntityType() { description = todoItem.Description, ordinal = todoItem.Ordinal, - createdDate = DateTime.Now + createdDate = DateTime.Now } } }; diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeletingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeletingDataTests.cs index d609f21fe8..46ac64810e 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeletingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DeletingDataTests.cs @@ -28,7 +28,7 @@ public DeletingDataTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Included.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Included.cs index 6c5e4f863c..d17546e1e7 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Included.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Included.cs @@ -37,7 +37,7 @@ public Included(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); _todoItemCollectionFaker = new Faker() .RuleFor(t => t.Name, f => f.Company.CatchPhrase()); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/PagingTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/PagingTests.cs index 47b0ec6b3b..f8d412a153 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/PagingTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/PagingTests.cs @@ -37,7 +37,7 @@ public PagingTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); _todoItemCollectionFaker = new Faker() .RuleFor(t => t.Name, f => f.Company.CatchPhrase()); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs index b978c89ead..76efe6f068 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/DocumentTests/Relationships.cs @@ -30,7 +30,7 @@ public Relationships(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs index 25654b7efb..fc57326d55 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs @@ -34,7 +34,7 @@ public FetchingDataTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); _personFaker = new Faker() .RuleFor(p => p.FirstName, f => f.Name.FirstName()) .RuleFor(p => p.LastName, f => f.Name.LastName()); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingRelationshipsTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingRelationshipsTests.cs index dedb0cf59f..6825f98751 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingRelationshipsTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingRelationshipsTests.cs @@ -28,7 +28,7 @@ public FetchingRelationshipsTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs index 38f41124aa..6f57a17e2b 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/SparseFieldSetTests.cs @@ -37,8 +37,8 @@ public async Task Can_Select_Sparse_Fieldsets() var todoItem = new TodoItem { Description = "description", Ordinal = 1, - CreatedDate = System.DateTime.Now, - AchievedDate = System.DateTime.Now.AddDays(2) + CreatedDate = System.DateTime.Now, + AchievedDate = System.DateTime.Now.AddDays(2) }; _dbContext.TodoItems.Add(todoItem); await _dbContext.SaveChangesAsync(); @@ -58,8 +58,8 @@ public async Task Can_Select_Sparse_Fieldsets() // assert Assert.Equal(0, result.Ordinal); Assert.Equal(todoItem.Description, result.Description); - Assert.Equal(todoItem.CreatedDate.ToString("G"), result.CreatedDate.ToString("G")); - Assert.Equal(todoItem.AchievedDate.GetValueOrDefault().ToString("G"), result.AchievedDate.GetValueOrDefault().ToString("G")); + Assert.Equal(todoItem.CreatedDate.ToString("G"), result.CreatedDate.ToString("G")); + Assert.Equal(todoItem.AchievedDate.GetValueOrDefault().ToString("G"), result.AchievedDate.GetValueOrDefault().ToString("G")); Assert.Equal(expectedSql, resultSql); } @@ -70,7 +70,7 @@ public async Task Fields_Query_Selects_Sparse_Field_Sets() var todoItem = new TodoItem { Description = "description", Ordinal = 1, - CreatedDate = System.DateTime.Now + CreatedDate = System.DateTime.Now }; _dbContext.TodoItems.Add(todoItem); await _dbContext.SaveChangesAsync(); @@ -93,7 +93,7 @@ public async Task Fields_Query_Selects_Sparse_Field_Sets() Assert.Equal(todoItem.StringId, deserializeBody.Data.Id); Assert.Equal(2, deserializeBody.Data.Attributes.Count); Assert.Equal(todoItem.Description, deserializeBody.Data.Attributes["description"]); - Assert.Equal(todoItem.CreatedDate, deserializeBody.Data.Attributes["created-date"]); + Assert.Equal(todoItem.CreatedDate, deserializeBody.Data.Attributes["created-date"]); } } } diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs index 2377389d7b..5e8c0f3634 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Net; using System.Net.Http; @@ -34,7 +34,7 @@ public UpdatingDataTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); _personFaker = new Faker() .RuleFor(p => p.FirstName, f => f.Name.FirstName()) .RuleFor(p => p.LastName, f => f.Name.LastName()); @@ -61,7 +61,7 @@ public async Task Respond_404_If_EntityDoesNotExist() { description = todoItem.Description, ordinal = todoItem.Ordinal, - createdDate = DateTime.Now + createdDate = DateTime.Now } } }; @@ -104,7 +104,7 @@ public async Task Can_Patch_Entity_And_HasOne_Relationships() { description = todoItem.Description, ordinal = todoItem.Ordinal, - createdDate = DateTime.Now + createdDate = DateTime.Now }, relationships = new { diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs index 18b2ba214d..c8896e0ff5 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingRelationshipsTests.cs @@ -40,7 +40,7 @@ public UpdatingRelationshipsTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs index 7b8fbe9b62..13c6d8e78c 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/TodoItemsControllerTests.cs @@ -35,7 +35,7 @@ public TodoItemControllerTests(DocsFixture fixture) _todoItemFaker = new Faker() .RuleFor(t => t.Description, f => f.Lorem.Sentence()) .RuleFor(t => t.Ordinal, f => f.Random.Number()) - .RuleFor(t => t.CreatedDate, f => f.Date.Past()); + .RuleFor(t => t.CreatedDate, f => f.Date.Past()); } [Fact] @@ -266,8 +266,8 @@ public async Task Can_Get_TodoItem_ById() Assert.Equal(todoItem.Id, deserializedBody.Id); Assert.Equal(todoItem.Description, deserializedBody.Description); Assert.Equal(todoItem.Ordinal, deserializedBody.Ordinal); - Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); - Assert.Equal(null, deserializedBody.AchievedDate); + Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(null, deserializedBody.AchievedDate); } [Fact] @@ -299,8 +299,8 @@ public async Task Can_Get_TodoItem_WithOwner() Assert.Equal(todoItem.Id, deserializedBody.Id); Assert.Equal(todoItem.Description, deserializedBody.Description); Assert.Equal(todoItem.Ordinal, deserializedBody.Ordinal); - Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); - Assert.Equal(null, deserializedBody.AchievedDate); + Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(null, deserializedBody.AchievedDate); } [Fact] @@ -319,9 +319,9 @@ public async Task Can_Post_TodoItem() type = "todo-items", attributes = new Dictionary() { - { "description", todoItem.Description }, - { "ordinal", todoItem.Ordinal }, - { "created-date", todoItem.CreatedDate } + { "description", todoItem.Description }, + { "ordinal", todoItem.Ordinal }, + { "created-date", todoItem.CreatedDate } }, relationships = new { @@ -354,8 +354,8 @@ public async Task Can_Post_TodoItem() // Assert Assert.Equal(HttpStatusCode.Created, response.StatusCode); Assert.Equal(todoItem.Description, deserializedBody.Description); - Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); - Assert.Equal(null, deserializedBody.AchievedDate); + Assert.Equal(todoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(null, deserializedBody.AchievedDate); } [Fact] @@ -379,12 +379,66 @@ public async Task Can_Patch_TodoItem() { type = "todo-items", attributes = new Dictionary() - { - { "description", newTodoItem.Description }, - { "ordinal", newTodoItem.Ordinal }, - { "created-date", newTodoItem.CreatedDate } - } - } + { + { "description", newTodoItem.Description }, + { "ordinal", newTodoItem.Ordinal }, + { "created-date", newTodoItem.CreatedDate } + } + } + }; + + var httpMethod = new HttpMethod("PATCH"); + var route = $"/api/v1/todo-items/{todoItem.Id}"; + + var request = new HttpRequestMessage(httpMethod, route); + request.Content = new StringContent(JsonConvert.SerializeObject(content)); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json"); + + var description = new RequestProperties("Patch TodoItem"); + + // Act + var response = await _fixture.MakeRequest(description, request); + var body = await response.Content.ReadAsStringAsync(); + var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(newTodoItem.Description, deserializedBody.Description); + Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal); + Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(null, deserializedBody.AchievedDate); + } + + [Fact] + public async Task Can_Patch_TodoItemWithNullable() + { + // Arrange + var person = new Person(); + _context.People.Add(person); + _context.SaveChanges(); + + var todoItem = _todoItemFaker.Generate(); + todoItem.AchievedDate = System.DateTime.Now; + todoItem.Owner = person; + _context.TodoItems.Add(todoItem); + _context.SaveChanges(); + + var newTodoItem = _todoItemFaker.Generate(); + newTodoItem.AchievedDate = System.DateTime.Now.AddDays(2); + + var content = new + { + data = new + { + type = "todo-items", + attributes = new Dictionary() + { + { "description", newTodoItem.Description }, + { "ordinal", newTodoItem.Ordinal }, + { "created-date", newTodoItem.CreatedDate }, + { "achieved-date", newTodoItem.AchievedDate } + } + } }; var httpMethod = new HttpMethod("PATCH"); @@ -405,116 +459,62 @@ public async Task Can_Patch_TodoItem() Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(newTodoItem.Description, deserializedBody.Description); Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal); - Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); - Assert.Equal(null, deserializedBody.AchievedDate); + Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(newTodoItem.AchievedDate.GetValueOrDefault().ToString("G"), deserializedBody.AchievedDate.GetValueOrDefault().ToString("G")); } - [Fact] - public async Task Can_Patch_TodoItemWithNullable() - { - // Arrange - var person = new Person(); - _context.People.Add(person); - _context.SaveChanges(); - - var todoItem = _todoItemFaker.Generate(); - todoItem.AchievedDate = System.DateTime.Now; - todoItem.Owner = person; - _context.TodoItems.Add(todoItem); - _context.SaveChanges(); - - var newTodoItem = _todoItemFaker.Generate(); - newTodoItem.AchievedDate = System.DateTime.Now.AddDays(2); - - var content = new - { - data = new - { - type = "todo-items", - attributes = new Dictionary() - { - { "description", newTodoItem.Description }, - { "ordinal", newTodoItem.Ordinal }, - { "created-date", newTodoItem.CreatedDate }, - { "achieved-date", newTodoItem.AchievedDate } - } - } - }; - - var httpMethod = new HttpMethod("PATCH"); - var route = $"/api/v1/todo-items/{todoItem.Id}"; - - var request = new HttpRequestMessage(httpMethod, route); - request.Content = new StringContent(JsonConvert.SerializeObject(content)); - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json"); - - var description = new RequestProperties("Patch TodoItem"); - - // Act - var response = await _fixture.MakeRequest(description, request); - var body = await response.Content.ReadAsStringAsync(); - var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(newTodoItem.Description, deserializedBody.Description); - Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal); - Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); - Assert.Equal(newTodoItem.AchievedDate.GetValueOrDefault().ToString("G"), deserializedBody.AchievedDate.GetValueOrDefault().ToString("G")); - } - - [Fact] - public async Task Can_Patch_TodoItemWithNullValue() - { - // Arrange - var person = new Person(); - _context.People.Add(person); - _context.SaveChanges(); - - var todoItem = _todoItemFaker.Generate(); - todoItem.AchievedDate = System.DateTime.Now; - todoItem.Owner = person; - _context.TodoItems.Add(todoItem); - _context.SaveChanges(); - - var newTodoItem = _todoItemFaker.Generate(); - - var content = new - { - data = new - { - type = "todo-items", - attributes = new Dictionary() - { - { "description", newTodoItem.Description }, - { "ordinal", newTodoItem.Ordinal }, - { "created-date", newTodoItem.CreatedDate }, - { "achieved-date", null } - } - } - }; - - var httpMethod = new HttpMethod("PATCH"); - var route = $"/api/v1/todo-items/{todoItem.Id}"; - - var request = new HttpRequestMessage(httpMethod, route); - request.Content = new StringContent(JsonConvert.SerializeObject(content)); - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json"); - - var description = new RequestProperties("Patch TodoItem"); - - // Act - var response = await _fixture.MakeRequest(description, request); - var body = await response.Content.ReadAsStringAsync(); - var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body); - - // Assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(newTodoItem.Description, deserializedBody.Description); - Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal); - Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); - Assert.Equal(null, deserializedBody.AchievedDate); - } + [Fact] + public async Task Can_Patch_TodoItemWithNullValue() + { + // Arrange + var person = new Person(); + _context.People.Add(person); + _context.SaveChanges(); + + var todoItem = _todoItemFaker.Generate(); + todoItem.AchievedDate = System.DateTime.Now; + todoItem.Owner = person; + _context.TodoItems.Add(todoItem); + _context.SaveChanges(); + + var newTodoItem = _todoItemFaker.Generate(); + + var content = new + { + data = new + { + type = "todo-items", + attributes = new Dictionary() + { + { "description", newTodoItem.Description }, + { "ordinal", newTodoItem.Ordinal }, + { "created-date", newTodoItem.CreatedDate }, + { "achieved-date", null } + } + } + }; + + var httpMethod = new HttpMethod("PATCH"); + var route = $"/api/v1/todo-items/{todoItem.Id}"; + + var request = new HttpRequestMessage(httpMethod, route); + request.Content = new StringContent(JsonConvert.SerializeObject(content)); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json"); + + var description = new RequestProperties("Patch TodoItem"); + + // Act + var response = await _fixture.MakeRequest(description, request); + var body = await response.Content.ReadAsStringAsync(); + var deserializedBody = (TodoItem)_fixture.GetService().Deserialize(body); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(newTodoItem.Description, deserializedBody.Description); + Assert.Equal(newTodoItem.Ordinal, deserializedBody.Ordinal); + Assert.Equal(newTodoItem.CreatedDate.ToString("G"), deserializedBody.CreatedDate.ToString("G")); + Assert.Equal(null, deserializedBody.AchievedDate); + } [Fact] public async Task Can_Delete_TodoItem() From d89dd2395e201f51de6809fb9cd02c29d0589c29 Mon Sep 17 00:00:00 2001 From: jaredcnance Date: Wed, 26 Apr 2017 17:50:51 -0500 Subject: [PATCH 4/4] refactor(type-helper): remove reflection dep and simplify null check --- src/JsonApiDotNetCore/Internal/TypeHelper.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Internal/TypeHelper.cs b/src/JsonApiDotNetCore/Internal/TypeHelper.cs index 3876ae5367..a0bb133e01 100644 --- a/src/JsonApiDotNetCore/Internal/TypeHelper.cs +++ b/src/JsonApiDotNetCore/Internal/TypeHelper.cs @@ -1,5 +1,4 @@ using System; -using System.Reflection; namespace JsonApiDotNetCore.Internal { @@ -10,8 +9,7 @@ public static object ConvertType(object value, Type type) if(value == null) return null; - if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) - type = Nullable.GetUnderlyingType(type); + type = Nullable.GetUnderlyingType(type) ?? type; var stringValue = value.ToString();