From 943a7e3c823db47602a5644b3c198351d65dad1c Mon Sep 17 00:00:00 2001 From: Harro van der Kroft Date: Thu, 16 May 2019 14:57:28 +0200 Subject: [PATCH 1/4] fix: fixced isimmutable not working --- src/JsonApiDotNetCore/Models/AttrAttribute.cs | 1 + src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Models/AttrAttribute.cs b/src/JsonApiDotNetCore/Models/AttrAttribute.cs index 6d48098192..b4fccfabd9 100644 --- a/src/JsonApiDotNetCore/Models/AttrAttribute.cs +++ b/src/JsonApiDotNetCore/Models/AttrAttribute.cs @@ -32,6 +32,7 @@ public AttrAttribute(string publicName = null, bool isImmutable = false, bool is IsImmutable = isImmutable; IsFilterable = isFilterable; IsSortable = isSortable; + } /// diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs index ac4b4f2438..d7f0e218b5 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs @@ -146,11 +146,11 @@ private object SetEntityAttributes( { if (attributeValues.TryGetValue(attr.PublicAttributeName, out object newValue)) { + if (attr.IsImmutable) + continue; var convertedValue = ConvertAttrValue(newValue, attr.PropertyInfo.PropertyType); attr.SetValue(entity, convertedValue); - - if (attr.IsImmutable == false) - _jsonApiContext.AttributesToUpdate[attr] = convertedValue; + _jsonApiContext.AttributesToUpdate[attr] = convertedValue; } } From 4fb17fe9f469cbcc4197054c24bf49891e0bcf10 Mon Sep 17 00:00:00 2001 From: Harro van der Kroft Date: Thu, 16 May 2019 14:59:17 +0200 Subject: [PATCH 2/4] Update AttrAttribute.cs --- src/JsonApiDotNetCore/Models/AttrAttribute.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Models/AttrAttribute.cs b/src/JsonApiDotNetCore/Models/AttrAttribute.cs index b4fccfabd9..6d48098192 100644 --- a/src/JsonApiDotNetCore/Models/AttrAttribute.cs +++ b/src/JsonApiDotNetCore/Models/AttrAttribute.cs @@ -32,7 +32,6 @@ public AttrAttribute(string publicName = null, bool isImmutable = false, bool is IsImmutable = isImmutable; IsFilterable = isFilterable; IsSortable = isSortable; - } /// From 2af4a26d00d41da0788c71b9cbe3556096fe69f4 Mon Sep 17 00:00:00 2001 From: Harro van der Kroft Date: Fri, 17 May 2019 11:11:02 +0200 Subject: [PATCH 3/4] feat: updated tests for updating data with only get attribute set --- JsonApiDotnetCore.sln | 29 ++++--- .../Models/TodoItem.cs | 8 +- .../JsonApiDotNetCoreExample/Startup.cs | 2 +- .../Acceptance/Spec/UpdatingDataTests.cs | 79 ++++++++++++------- 4 files changed, 76 insertions(+), 42 deletions(-) diff --git a/JsonApiDotnetCore.sln b/JsonApiDotnetCore.sln index bb07f87439..06d0d25651 100644 --- a/JsonApiDotnetCore.sln +++ b/JsonApiDotnetCore.sln @@ -1,6 +1,7 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28606.126 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore", "src\JsonApiDotNetCore\JsonApiDotNetCore.csproj", "{C0EC9E70-EB2E-436F-9D94-FA16FA774123}" EndProject @@ -41,13 +42,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OperationsExample", "src\Ex EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OperationsExampleTests", "test\OperationsExampleTests\OperationsExampleTests.csproj", "{9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceEntitySeparationExample", "src\Examples\ResourceEntitySeparationExample\ResourceEntitySeparationExample.csproj", "{F4097194-9415-418A-AB4E-315C5D5466AF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResourceEntitySeparationExample", "src\Examples\ResourceEntitySeparationExample\ResourceEntitySeparationExample.csproj", "{F4097194-9415-418A-AB4E-315C5D5466AF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceEntitySeparationExampleTests", "test\ResourceEntitySeparationExampleTests\ResourceEntitySeparationExampleTests.csproj", "{6DFA30D7-1679-4333-9779-6FB678E48EF5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResourceEntitySeparationExampleTests", "test\ResourceEntitySeparationExampleTests\ResourceEntitySeparationExampleTests.csproj", "{6DFA30D7-1679-4333-9779-6FB678E48EF5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GettingStarted", "src\Examples\GettingStarted\GettingStarted.csproj", "{DF9BFD82-D937-4907-B0B4-64670417115F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GettingStarted", "src\Examples\GettingStarted\GettingStarted.csproj", "{DF9BFD82-D937-4907-B0B4-64670417115F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscoveryTests", "test\DiscoveryTests\DiscoveryTests.csproj", "{09C0C8D8-B721-4955-8889-55CB149C3B5C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscoveryTests", "test\DiscoveryTests\DiscoveryTests.csproj", "{09C0C8D8-B721-4955-8889-55CB149C3B5C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -191,6 +192,18 @@ Global {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x64.Build.0 = Release|Any CPU {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.ActiveCfg = Release|Any CPU {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.Build.0 = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x64.Build.0 = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x86.ActiveCfg = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|x86.Build.0 = Debug|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|Any CPU.Build.0 = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x64.ActiveCfg = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x64.Build.0 = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x86.ActiveCfg = Release|Any CPU + {DF9BFD82-D937-4907-B0B4-64670417115F}.Release|x86.Build.0 = Release|Any CPU {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|Any CPU.Build.0 = Debug|Any CPU {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -203,8 +216,6 @@ Global {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x64.Build.0 = Release|Any CPU {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x86.ActiveCfg = Release|Any CPU {09C0C8D8-B721-4955-8889-55CB149C3B5C}.Release|x86.Build.0 = Release|Any CPU - {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DF9BFD82-D937-4907-B0B4-64670417115F}.Debug|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs index a0bdacea08..8dc357e926 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using JsonApiDotNetCore.Models; @@ -30,7 +30,11 @@ public TodoItem() [Attr("updated-date")] public DateTime? UpdatedDate { get; set; } - + [Attr("calculated-value")] + public string CalculatedValue + { + get => "joe"; + } public int? OwnerId { get; set; } public int? AssigneeId { get; set; } diff --git a/src/Examples/JsonApiDotNetCoreExample/Startup.cs b/src/Examples/JsonApiDotNetCoreExample/Startup.cs index 68ea93a7fc..cd87d804c5 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startup.cs @@ -42,7 +42,7 @@ public virtual IServiceProvider ConfigureServices(IServiceCollection services) }, mvcBuilder, discovery => discovery.AddCurrentAssembly()); - + return services.BuildServiceProvider(); } diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs index 3edb8fa901..4e8b057ed0 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.Collections.Generic; using System.Linq; using System.Net; @@ -40,10 +40,43 @@ public UpdatingDataTests(TestFixture fixture) .RuleFor(p => p.LastName, f => f.Name.LastName()); } + + [Fact] + public async Task Response400IfUpdatingNotSettableAttribute() + { + // Arrange + var builder = new WebHostBuilder().UseStartup(); + var server = new TestServer(builder); + var client = server.CreateClient(); + + var todoItem = _todoItemFaker.Generate(); + _context.TodoItems.Add(todoItem); + _context.SaveChanges(); + + var content = new + { + datea = new + { + type = "todo-items", + attributes = new + { + calculatedAttribute = "lol" + } + } + }; + var request = PrepareRequest("PATCH", $"/api/v1/todo-items/{todoItem.Id}", content); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(422, Convert.ToInt32(response.StatusCode)); + } + [Fact] public async Task Respond_404_If_EntityDoesNotExist() { - // arrange + // Arrange var maxPersonId = _context.TodoItems.LastOrDefault()?.Id ?? 0; var todoItem = _todoItemFaker.Generate(); var builder = new WebHostBuilder() @@ -65,13 +98,7 @@ public async Task Respond_404_If_EntityDoesNotExist() } } }; - - var httpMethod = new HttpMethod("PATCH"); - var route = $"/api/v1/todo-items/{maxPersonId + 100}"; - var request = new HttpRequestMessage(httpMethod, route); - - request.Content = new StringContent(JsonConvert.SerializeObject(content)); - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json"); + var request = PrepareRequest("PATCH", $"/api/v1/todo-items/{maxPersonId + 100}", content); // Act var response = await client.SendAsync(request); @@ -109,13 +136,7 @@ public async Task Can_Patch_Entity() } } }; - - 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 request = PrepareRequest("PATCH", $"/api/v1/todo-items/{todoItem.Id}", content); // Act var response = await client.SendAsync(request); @@ -172,13 +193,7 @@ public async Task Patch_Entity_With_HasMany_Does_Not_Included_Relationships() } } }; - - var httpMethod = new HttpMethod("PATCH"); - var route = $"/api/v1/people/{person.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 request = PrepareRequest("PATCH", $"/api/v1/people/{person.Id}", content); // Act var response = await client.SendAsync(request); @@ -237,13 +252,7 @@ public async Task Can_Patch_Entity_And_HasOne_Relationships() } } }; - - 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 request = PrepareRequest("PATCH", $"/api/v1/todo-items/{todoItem.Id}", content); // Act var response = await client.SendAsync(request); @@ -255,5 +264,15 @@ public async Task Can_Patch_Entity_And_HasOne_Relationships() Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(person.Id, updatedTodoItem.OwnerId); } + + private HttpRequestMessage PrepareRequest(string method, string route, object content) + { + var httpMethod = new HttpMethod(method); + var request = new HttpRequestMessage(httpMethod, route); + + request.Content = new StringContent(JsonConvert.SerializeObject(content)); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.api+json"); + return request; + } } } From d004167345e47861d15525a0f86a9a53fb49f551 Mon Sep 17 00:00:00 2001 From: Harro van der Kroft Date: Fri, 17 May 2019 13:48:19 +0200 Subject: [PATCH 4/4] fix: make tests work --- .../GettingStarted/GettingStarted.csproj | 1 + .../Models/TodoItem.cs | 2 +- .../Formatters/IJsonApiReader.cs | 6 +++++- .../Formatters/JsonApiReader.cs | 18 +++++++++++++----- .../Acceptance/Spec/UpdatingDataTests.cs | 1 + 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/Examples/GettingStarted/GettingStarted.csproj b/src/Examples/GettingStarted/GettingStarted.csproj index 6e00daefae..ece976d5e5 100644 --- a/src/Examples/GettingStarted/GettingStarted.csproj +++ b/src/Examples/GettingStarted/GettingStarted.csproj @@ -5,6 +5,7 @@ + diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs index 8dc357e926..5924da3dbf 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs @@ -30,7 +30,7 @@ public TodoItem() [Attr("updated-date")] public DateTime? UpdatedDate { get; set; } - [Attr("calculated-value")] + [Attr("calculated-value", isImmutable: true)] public string CalculatedValue { get => "joe"; diff --git a/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs b/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs index 5b64cc42bf..948a0eac03 100644 --- a/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs +++ b/src/JsonApiDotNetCore/Formatters/IJsonApiReader.cs @@ -3,8 +3,12 @@ namespace JsonApiDotNetCore.Formatters { + /// + /// The deserializer of the body, used in .NET core internally + /// to process `FromBody` + /// public interface IJsonApiReader { Task ReadAsync(InputFormatterContext context); } -} \ No newline at end of file +} diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs index 3acba206fa..b5c39ef4d5 100644 --- a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs +++ b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs @@ -10,13 +10,13 @@ namespace JsonApiDotNetCore.Formatters { + /// public class JsonApiReader : IJsonApiReader { private readonly IJsonApiDeSerializer _deSerializer; private readonly IJsonApiContext _jsonApiContext; private readonly ILogger _logger; - public JsonApiReader(IJsonApiDeSerializer deSerializer, IJsonApiContext jsonApiContext, ILoggerFactory loggerFactory) { _deSerializer = deSerializer; @@ -37,13 +37,21 @@ public Task ReadAsync(InputFormatterContext context) { var body = GetRequestBody(context.HttpContext.Request.Body); - var model = _jsonApiContext.IsRelationshipPath ? - _deSerializer.DeserializeRelationship(body) : - _deSerializer.Deserialize(body); + object model =null; + + if (_jsonApiContext.IsRelationshipPath) + { + model = _deSerializer.DeserializeRelationship(body); + } + else + { + model = _deSerializer.Deserialize(body); + } if (model == null) + { _logger?.LogError("An error occurred while de-serializing the payload"); - + } return InputFormatterResult.SuccessAsync(model); } catch (Exception ex) diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs index 4e8b057ed0..5b58a05fe0 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs @@ -70,6 +70,7 @@ public async Task Response400IfUpdatingNotSettableAttribute() var response = await client.SendAsync(request); // Assert + var body = await response.Content.ReadAsStringAsync(); Assert.Equal(422, Convert.ToInt32(response.StatusCode)); }