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(); diff --git a/src/JsonApiDotNetCore/Models/AttrAttribute.cs b/src/JsonApiDotNetCore/Models/AttrAttribute.cs index 3ce9a2196a..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 { @@ -26,10 +27,13 @@ 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) + { + var convertedValue = TypeHelper.ConvertType(newValue, propertyInfo.PropertyType); + + propertyInfo.SetValue(entity, convertedValue); + } } } } diff --git a/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 9d691c8268..a54c2a93e8 100644 --- a/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -1,6 +1,8 @@ +using JsonApiDotNetCoreExample.Models; using JsonApiDotNetCore.Models; using JsonApiDotNetCoreExample.Models; using Microsoft.EntityFrameworkCore; +using System; namespace JsonApiDotNetCoreExample.Data { @@ -12,6 +14,9 @@ public AppDbContext(DbContextOptions options) protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Entity() + .Property(t => t.CreatedDate).HasDefaultValueSql("CURRENT_TIMESTAMP").IsRequired(); + modelBuilder.Entity() .HasOne(t => t.Assignee) .WithMany(p => p.AssignedTodoItems) 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..7257835791 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..42c3f0b1f1 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 866b9a331b..08db60d635 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..46ac64810e 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 16971d8b06..d17546e1e7 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..f8d412a153 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..76efe6f068 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..fc57326d55 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..6825f98751 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..6f57a17e2b 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..5e8c0f3634 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..c8896e0ff5 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..13c6d8e78c 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,10 +378,11 @@ public async Task Can_Patch_TodoItem() data = new { type = "todo-items", - attributes = new + attributes = new Dictionary() { - description = newTodoItem.Description, - ordinal = newTodoItem.Ordinal + { "description", newTodoItem.Description }, + { "ordinal", newTodoItem.Ordinal }, + { "created-date", newTodoItem.CreatedDate } } } }; @@ -396,6 +405,115 @@ 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]