From cfddf4b9ef30c4873539b7fb9e4d0c5ad14becd6 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Wed, 30 Oct 2019 20:30:39 +0100 Subject: [PATCH 1/8] Fix/deviating dbset name (#603) * test: expose dbset name bug * fix: deviating dbset name * chore: add launchSettings.json to gitignore * chore: delete launchSettings.json from git --- .../Data/AppDbContext.cs | 2 +- .../NoEntityFrameworkExample/.gitignore | 2 ++ .../Properties/launchSettings.json | 27 ------------------- .../Builders/ResourceGraphBuilder.cs | 2 +- .../Acceptance/ManyToManyTests.cs | 2 +- .../IServiceCollectionExtensionsTests.cs | 25 +++++++++++++++-- 6 files changed, 28 insertions(+), 32 deletions(-) delete mode 100644 src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs index a1887ba235..7faa22bd55 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -11,7 +11,7 @@ public class AppDbContext : DbContext public DbSet TodoItemCollections { get; set; } public DbSet CamelCasedModels { get; set; } public DbSet
Articles { get; set; } - public DbSet Authors { get; set; } + public DbSet AuthorDifferentDbContextName { get; set; } public DbSet NonJsonApiResources { get; set; } public DbSet Users { get; set; } public DbSet PersonRoles { get; set; } diff --git a/src/Examples/NoEntityFrameworkExample/.gitignore b/src/Examples/NoEntityFrameworkExample/.gitignore index 0ca27f04e1..700191e656 100644 --- a/src/Examples/NoEntityFrameworkExample/.gitignore +++ b/src/Examples/NoEntityFrameworkExample/.gitignore @@ -22,6 +22,8 @@ bld/ [Bb]in/ [Oo]bj/ +Properties/launchSettings.json + # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot diff --git a/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json b/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json deleted file mode 100644 index 1dff6cfe69..0000000000 --- a/src/Examples/NoEntityFrameworkExample/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:57181/", - "sslPort": 0 - } - }, - "profiles": { - "NoEntityFrameworkExample": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:5000/" - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs index 405fe64936..e025d4f7de 100644 --- a/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/ResourceGraphBuilder.cs @@ -218,7 +218,7 @@ private string GetResourceNameFromDbSetProperty(PropertyInfo property, Type reso // fallback to the established convention using the DbSet Property.Name // e.g DbSet FooBars { get; set; } => "foo-bars" - return _resourceNameFormatter.ApplyCasingConvention(property.Name); + return _resourceNameFormatter.FormatResourceName(resourceType); } private (bool isJsonApiResource, Type idType) GetIdType(Type resourceType) diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/ManyToManyTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/ManyToManyTests.cs index 1df5593fb0..6b6bb526a3 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/ManyToManyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/ManyToManyTests.cs @@ -240,7 +240,7 @@ public async Task Can_Create_Many_To_Many() var tag = _tagFaker.Generate(); var author = new Author(); context.Tags.Add(tag); - context.Authors.Add(author); + context.AuthorDifferentDbContextName.Add(author); await context.SaveChangesAsync(); var article = _articleFaker.Generate(); diff --git a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs index d23b1f4d9d..20fa848b35 100644 --- a/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs +++ b/test/UnitTests/Extensions/IServiceCollectionExtensionsTests.cs @@ -59,6 +59,27 @@ public void AddJsonApiInternals_Adds_All_Required_Services() Assert.NotNull(provider.GetService(typeof(RepositoryRelationshipUpdateHelper))); } + [Fact] + public void RegisterResource_DeviatingDbContextPropertyName_RegistersCorrectly() + { + // Arrange + var services = new ServiceCollection(); + + services.AddDbContext(options => options.UseInMemoryDatabase("UnitTestDb"), ServiceLifetime.Transient); + services.AddJsonApi(); + + // Act + // this is required because the DbContextResolver requires access to the current HttpContext + // to get the request scoped DbContext instance + services.AddScoped(); + var provider = services.BuildServiceProvider(); + var graph = provider.GetService(); + var resourceContext = graph.GetResourceContext(); + + // Assert + Assert.Equal("authors", resourceContext.ResourceName); + } + [Fact] public void AddResourceService_Registers_All_Shorthand_Service_Interfaces() { @@ -116,7 +137,7 @@ public void AddResourceService_Throws_If_Type_Does_Not_Implement_Any_Interfaces( } [Fact] - public void AddJsonApi_With_Context_Uses_DbSet_PropertyName_If_NoOtherSpecified() + public void AddJsonApi_With_Context_Uses_Resource_Type_Name_If_NoOtherSpecified() { // Arrange var services = new ServiceCollection(); @@ -130,7 +151,7 @@ public void AddJsonApi_With_Context_Uses_DbSet_PropertyName_If_NoOtherSpecified( var provider = services.BuildServiceProvider(); var resourceGraph = provider.GetService(); var resource = resourceGraph.GetResourceContext(typeof(IntResource)); - Assert.Equal("resource", resource.ResourceName); + Assert.Equal("int-resources", resource.ResourceName); } public class IntResource : Identifiable { } From 5733fe5c37074b9bbfde284c916ea368a5a0fe6e Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Thu, 31 Oct 2019 10:42:39 +0100 Subject: [PATCH 2/8] Fix/pagesize (#601) * feat: new startup for test * chore: rename startup class * test: reproduce #599 in test * fix: zero division error * chore: remove redundant DefaultPageSize on IPageService * chore: update .gitignore --- .../Startups/ClientGeneratedIdsStartup.cs | 6 +-- .../Startups/NoDefaultPageSizeStartup.cs | 43 +++++++++++++++++++ .../Contracts/IPageService.cs | 6 --- .../QueryParameterServices/PageService.cs | 5 +-- .../Acceptance/Spec/FetchingDataTests.cs | 29 ++++++++++++- 5 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Startups/ClientGeneratedIdsStartup.cs b/src/Examples/JsonApiDotNetCoreExample/Startups/ClientGeneratedIdsStartup.cs index 10255d6727..8f60d43a62 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startups/ClientGeneratedIdsStartup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startups/ClientGeneratedIdsStartup.cs @@ -15,8 +15,8 @@ namespace JsonApiDotNetCoreExample public class ClientGeneratedIdsStartup : Startup { public ClientGeneratedIdsStartup(IWebHostEnvironment env) - : base (env) - { } + : base(env) + { } public override void ConfigureServices(IServiceCollection services) { @@ -41,4 +41,4 @@ public override void ConfigureServices(IServiceCollection services) mvcBuilder: mvcBuilder); } } -} +} \ No newline at end of file diff --git a/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs b/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs new file mode 100644 index 0000000000..a15b71c2c5 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using JsonApiDotNetCoreExample.Data; +using Microsoft.EntityFrameworkCore; +using JsonApiDotNetCore.Extensions; +using System.Reflection; + +namespace JsonApiDotNetCoreExample +{ + /// + /// This should be in JsonApiDotNetCoreExampleTests project but changes in .net core 3.0 + /// do no longer allow that. See https://github.com/aspnet/AspNetCore/issues/15373. + /// + public class NoDefaultPageSizeStartup : Startup + { + public NoDefaultPageSizeStartup(IWebHostEnvironment env) + : base(env) + { } + + public override void ConfigureServices(IServiceCollection services) + { + var loggerFactory = new LoggerFactory(); + var mvcBuilder = services.AddMvcCore(); + services + .AddSingleton(loggerFactory) + .AddLogging(builder => + { + builder.AddConsole(); + }) + .AddDbContext(options => options.UseNpgsql(GetDbConnectionString()), ServiceLifetime.Transient) + .AddJsonApi(options => { + options.Namespace = "api/v1"; + options.IncludeTotalRecordCount = true; + options.EnableResourceHooks = true; + options.LoaDatabaseValues = true; + options.AllowClientGeneratedIds = true; + }, + discovery => discovery.AddAssembly(Assembly.Load(nameof(JsonApiDotNetCoreExample))), + mvcBuilder: mvcBuilder); + } + } +} diff --git a/src/JsonApiDotNetCore/QueryParameterServices/Contracts/IPageService.cs b/src/JsonApiDotNetCore/QueryParameterServices/Contracts/IPageService.cs index 76f56baf6a..017773d3d6 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/Contracts/IPageService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/Contracts/IPageService.cs @@ -14,19 +14,13 @@ public interface IPageService : IQueryParameterService /// int PageSize { get; set; } /// - /// What is the default page size - /// - int DefaultPageSize { get; set; } - /// /// What page are we currently on /// int CurrentPage { get; set; } - /// /// Total amount of pages for request /// int TotalPages { get; } - /// /// Checks if pagination is enabled /// diff --git a/src/JsonApiDotNetCore/QueryParameterServices/PageService.cs b/src/JsonApiDotNetCore/QueryParameterServices/PageService.cs index d4aa5052ee..6f1559246c 100644 --- a/src/JsonApiDotNetCore/QueryParameterServices/PageService.cs +++ b/src/JsonApiDotNetCore/QueryParameterServices/PageService.cs @@ -15,7 +15,6 @@ public class PageService : QueryParameterService, IPageService public PageService(IJsonApiOptions options) { _options = options; - DefaultPageSize = _options.DefaultPageSize; PageSize = _options.DefaultPageSize; } /// @@ -23,11 +22,9 @@ public PageService(IJsonApiOptions options) /// public int PageSize { get; set; } /// - public int DefaultPageSize { get; set; } // I think we shouldnt expose this - /// public int CurrentPage { get; set; } /// - public int TotalPages => (TotalRecords == null) ? -1 : (int)Math.Ceiling(decimal.Divide(TotalRecords.Value, PageSize)); + public int TotalPages => (TotalRecords == null || PageSize == 0) ? -1 : (int)Math.Ceiling(decimal.Divide(TotalRecords.Value, PageSize)); /// public virtual void Parse(KeyValuePair queryParameter) diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs index 77be87e23a..40d82b8888 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/FetchingDataTests.cs @@ -1,4 +1,5 @@ -using System.Net; +using System.Linq; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using Bogus; @@ -96,5 +97,31 @@ public async Task Included_Records_Contain_Relationship_Links() Assert.Equal($"http://localhost/api/v1/people/{person.Id}/relationships/todo-items", deserializedBody.Included[0].Relationships["todo-items"].Links.Self); context.Dispose(); } + + [Fact] + public async Task GetResources_NoDefaultPageSize_ReturnsResources() + { + // Arrange + var context = _fixture.GetService(); + var todoItems = _todoItemFaker.Generate(20).ToList(); + context.TodoItems.AddRange(todoItems); + await context.SaveChangesAsync(); + + var builder = new WebHostBuilder() + .UseStartup(); + var httpMethod = new HttpMethod("GET"); + var route = $"/api/v1/todo-items"; + var server = new TestServer(builder); + var client = server.CreateClient(); + var request = new HttpRequestMessage(httpMethod, route); + + // Act + var response = await client.SendAsync(request); + var body = await response.Content.ReadAsStringAsync(); + var result = _fixture.GetDeserializer().DeserializeList(body); + + // Assert + Assert.True(result.Data.Count >= 20); + } } } From a4802b29a810b47f27c86e6b872bb358ca837b8d Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Fri, 1 Nov 2019 16:55:35 +0100 Subject: [PATCH 3/8] fix: typo LoaDatabaseValues (#608) * fix: typo * fix: typo in fix of typo * chore: enable sql logging in JsonApiDotNetCoreExample --- .../Startups/ClientGeneratedIdsStartup.cs | 2 +- .../Startups/NoDefaultPageSizeStartup.cs | 2 +- .../JsonApiDotNetCoreExample/Startups/Startup.cs | 9 +++++++-- src/Examples/JsonApiDotNetCoreExample/appsettings.json | 5 ++++- src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs | 2 +- src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs | 2 +- .../Hooks/Execution/HookExecutorHelper.cs | 2 +- test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs | 2 +- 8 files changed, 17 insertions(+), 9 deletions(-) mode change 100755 => 100644 src/Examples/JsonApiDotNetCoreExample/appsettings.json diff --git a/src/Examples/JsonApiDotNetCoreExample/Startups/ClientGeneratedIdsStartup.cs b/src/Examples/JsonApiDotNetCoreExample/Startups/ClientGeneratedIdsStartup.cs index 8f60d43a62..665096060c 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startups/ClientGeneratedIdsStartup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startups/ClientGeneratedIdsStartup.cs @@ -34,7 +34,7 @@ public override void ConfigureServices(IServiceCollection services) options.DefaultPageSize = 5; options.IncludeTotalRecordCount = true; options.EnableResourceHooks = true; - options.LoaDatabaseValues = true; + options.LoadDatabaseValues = true; options.AllowClientGeneratedIds = true; }, discovery => discovery.AddAssembly(Assembly.Load(nameof(JsonApiDotNetCoreExample))), diff --git a/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs b/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs index a15b71c2c5..f0ee0a6acb 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startups/NoDefaultPageSizeStartup.cs @@ -33,7 +33,7 @@ public override void ConfigureServices(IServiceCollection services) options.Namespace = "api/v1"; options.IncludeTotalRecordCount = true; options.EnableResourceHooks = true; - options.LoaDatabaseValues = true; + options.LoadDatabaseValues = true; options.AllowClientGeneratedIds = true; }, discovery => discovery.AddAssembly(Assembly.Load(nameof(JsonApiDotNetCoreExample))), diff --git a/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs b/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs index 3e9d3ca9e3..387287a243 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Startups/Startup.cs @@ -7,6 +7,8 @@ using Microsoft.EntityFrameworkCore; using JsonApiDotNetCore.Extensions; using System; +using Microsoft.Extensions.Logging.Console; +using Microsoft.Extensions.Logging.Debug; namespace JsonApiDotNetCoreExample { @@ -24,6 +26,7 @@ public Startup(IWebHostEnvironment env) Config = builder.Build(); } + public virtual void ConfigureServices(IServiceCollection services) { var loggerFactory = new LoggerFactory(); @@ -36,7 +39,9 @@ public virtual void ConfigureServices(IServiceCollection services) }) .AddDbContext(options => { - options.UseNpgsql(GetDbConnectionString(), options => options.SetPostgresVersion(new Version(9,6))); + options.UseLoggerFactory(new LoggerFactory(new[] { new DebugLoggerProvider() })) + .EnableSensitiveDataLogging() + .UseNpgsql(GetDbConnectionString(), options => options.SetPostgresVersion(new Version(9,6))); }, ServiceLifetime.Transient) .AddJsonApi(options => { @@ -44,7 +49,7 @@ public virtual void ConfigureServices(IServiceCollection services) options.DefaultPageSize = 5; options.IncludeTotalRecordCount = true; options.EnableResourceHooks = true; - options.LoaDatabaseValues = true; + options.LoadDatabaseValues = true; }, discovery => discovery.AddCurrentAssembly()); services.AddClientSerialization(); diff --git a/src/Examples/JsonApiDotNetCoreExample/appsettings.json b/src/Examples/JsonApiDotNetCoreExample/appsettings.json old mode 100755 new mode 100644 index c468439079..38f0280d9f --- a/src/Examples/JsonApiDotNetCoreExample/appsettings.json +++ b/src/Examples/JsonApiDotNetCoreExample/appsettings.json @@ -7,7 +7,10 @@ "LogLevel": { "Default": "Warning", "System": "Warning", - "Microsoft": "Warning" + "Microsoft": "Warning", + "LogLevel": { + "Microsoft.EntityFrameworkCore": "Debug" + } } } } diff --git a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs index a969a4dbf0..374441474d 100644 --- a/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/IJsonApiOptions.cs @@ -8,7 +8,7 @@ public interface IJsonApiOptions : ILinksConfiguration, ISerializerOptions /// /// Defaults to . /// - bool LoaDatabaseValues { get; set; } + bool LoadDatabaseValues { get; set; } /// /// Whether or not the total-record count should be included in all document /// level meta objects. diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs index fe41af6602..cb76720a75 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs @@ -52,7 +52,7 @@ public class JsonApiOptions : IJsonApiOptions /// /// Defaults to . /// - public bool LoaDatabaseValues { get; set; } = false; + public bool LoadDatabaseValues { get; set; } = false; /// /// The base URL Namespace diff --git a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs index 5e2ee7731d..af364d4ceb 100644 --- a/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs +++ b/src/JsonApiDotNetCore/Hooks/Execution/HookExecutorHelper.cs @@ -102,7 +102,7 @@ public bool ShouldLoadDbValues(Type entityType, ResourceHook hook) return false; if (discovery.DatabaseValuesEnabledHooks.Contains(hook)) return true; - return _options.LoaDatabaseValues; + return _options.LoadDatabaseValues; } bool ShouldExecuteHook(RightType entityType, ResourceHook hook) diff --git a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs index c341106d0e..ea7db285d6 100644 --- a/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs +++ b/test/UnitTests/ResourceHooks/ResourceHooksTestsSetup.cs @@ -145,7 +145,7 @@ public class HooksTestsSetup : HooksDummyData var pfMock = new Mock(); var ufMock = new Mock(); var iqsMock = new Mock(); - var optionsMock = new JsonApiOptions { LoaDatabaseValues = false }; + var optionsMock = new JsonApiOptions { LoadDatabaseValues = false }; return (ufMock, iqsMock, pfMock, optionsMock); } From dc7ae14f3d04c892e153b49b03f5fd518f4aa514 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 4 Nov 2019 12:07:49 +0100 Subject: [PATCH 4/8] Acceptance tests EF inheritance (#610) * chore: create inheritance model + controller * test: create and patch acceptance test * chore: close issue --- .../Controllers/UsersController.cs | 10 ++++++++ .../Data/AppDbContext.cs | 3 +++ .../JsonApiDotNetCoreExample/Models/User.cs | 5 ++++ .../Internal/DefaultRoutingConvention.cs | 1 + .../Acceptance/Spec/CreatingDataTests.cs | 17 +++++++++++++ .../Acceptance/Spec/EndToEndTest.cs | 5 ++++ .../Acceptance/Spec/UpdatingDataTests.cs | 25 ++++++++++++++++--- .../Acceptance/TestFixture.cs | 1 + 8 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/UsersController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/UsersController.cs index cc47e88d84..de1996d5e5 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Controllers/UsersController.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/UsersController.cs @@ -15,4 +15,14 @@ public UsersController( : base(jsonApiOptions, resourceService, loggerFactory) { } } + + public class SuperUsersController : JsonApiController + { + public SuperUsersController( + IJsonApiOptions jsonApiOptions, + IResourceService resourceService, + ILoggerFactory loggerFactory) + : base(jsonApiOptions, resourceService, loggerFactory) + { } + } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 7faa22bd55..9411f48a4f 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -14,6 +14,7 @@ public class AppDbContext : DbContext public DbSet AuthorDifferentDbContextName { get; set; } public DbSet NonJsonApiResources { get; set; } public DbSet Users { get; set; } + public DbSet SuperUsers { get; set; } public DbSet PersonRoles { get; set; } public DbSet ArticleTags { get; set; } public DbSet IdentifiableArticleTags { get; set; } @@ -23,6 +24,8 @@ public AppDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Entity().HasBaseType(); + modelBuilder.Entity() .Property(t => t.CreatedDate).HasDefaultValueSql("CURRENT_TIMESTAMP").IsRequired(); diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/User.cs b/src/Examples/JsonApiDotNetCoreExample/Models/User.cs index f966cb84cd..d0e38b93e7 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/User.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/User.cs @@ -8,4 +8,9 @@ public class User : Identifiable [Attr] public string Username { get; set; } [Attr] public string Password { get; set; } } + + public class SuperUser : User + { + [Attr] public int SecurityLevel { get; set; } + } } diff --git a/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs b/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs index eb68142e31..f694d95010 100644 --- a/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Internal/DefaultRoutingConvention.cs @@ -58,6 +58,7 @@ public void Apply(ApplicationModel application) foreach (var controller in application.Controllers) { var resourceType = GetResourceTypeFromController(controller.ControllerType); + if (resourceType != null) _registeredResources.Add(controller.ControllerName, resourceType); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs index 32ac423eee..982dfdd100 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/CreatingDataTests.cs @@ -36,6 +36,23 @@ public CreatingDataTests(TestFixture fixture) : base(fixture) } + [Fact] + public async Task CreateResource_ModelWithEntityFrameworkInHeritance_IsCreated() + { + // Arrange + var dbContext = PrepareTest(); + var serializer = GetSerializer(e => new { e.SecurityLevel, e.Username, e.Password }); + var superUser = new SuperUser { SecurityLevel = 1337, Username = "Super", Password = "User" }; + + // Act + var (body, response) = await Post("/api/v1/super-users", serializer.Serialize(superUser)); + + // Assert + AssertEqualStatusCode(HttpStatusCode.Created, response); + var createdSuperUser = _deserializer.DeserializeSingle(body).Data; + var created = dbContext.SuperUsers.Where(e => e.Id.Equals(createdSuperUser.Id)).First(); + } + [Fact] public async Task CreateResource_GuidResource_IsCreated() { diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/EndToEndTest.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/EndToEndTest.cs index e287e1ae20..0f0d045bdc 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/EndToEndTest.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/EndToEndTest.cs @@ -61,6 +61,11 @@ public AppDbContext GetDbContext() return SendRequest("POST", route, content); } + public Task<(string, HttpResponseMessage)> Patch(string route, string content) + { + return SendRequest("PATCH", route, content); + } + public IRequestSerializer GetSerializer(Expression> attributes = null, Expression> relationships = null) where TResource : class, IIdentifiable { return _fixture.GetSerializer(attributes, relationships); diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs index 9860f4e2cf..9d58f4d75c 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/UpdatingDataTests.cs @@ -20,15 +20,15 @@ namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec { [Collection("WebHostCollection")] - public class UpdatingDataTests + public class UpdatingDataTests : EndToEndTest { private TestFixture _fixture; private AppDbContext _context; private Faker _todoItemFaker; private Faker _personFaker; - public UpdatingDataTests(TestFixture fixture) - { + public UpdatingDataTests(TestFixture fixture) : base(fixture) + { _fixture = fixture; _context = fixture.GetService(); @@ -41,6 +41,25 @@ public UpdatingDataTests(TestFixture fixture) .RuleFor(p => p.LastName, f => f.Name.LastName()); } + [Fact] + public async Task PatchResource_ModelWithEntityFrameworkInHeritance_IsPatched() + { + // Arrange + var dbContext = PrepareTest(); + var serializer = GetSerializer(e => new { e.SecurityLevel }); + var superUser = new SuperUser { SecurityLevel = 1337, Username = "Super", Password = "User" }; + dbContext.SuperUsers.Add(superUser); + dbContext.SaveChanges(); + var su = new SuperUser { Id = superUser.Id, SecurityLevel = 2674 }; + + // Act + var (body, response) = await Patch($"/api/v1/super-users/{su.Id}", serializer.Serialize(su)); + + // Assert + AssertEqualStatusCode(HttpStatusCode.OK, response); + var updated = _deserializer.DeserializeSingle(body).Data; + Assert.Equal(2674, updated.SecurityLevel); + } [Fact] public async Task Response400IfUpdatingNotSettableAttribute() diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs index a24bf58208..92cb977c58 100644 --- a/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs +++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/TestFixture.cs @@ -52,6 +52,7 @@ public IResponseDeserializer GetDeserializer() .AddResource() .AddResource() .AddResource() + .AddResource() .AddResource() .AddResource() .AddResource() From 382acce386d453233f32318334cd79e8a892b857 Mon Sep 17 00:00:00 2001 From: Maurits Moeys Date: Mon, 4 Nov 2019 17:33:08 +0100 Subject: [PATCH 5/8] Improve usability RequestSerializer (#613) * feat: remove inability to use request serializer with unknown type at runtime * fix: typo in test setup * chore: spacing * fix: add IResourceQueryService and IResourceCmdService to DI container in application builder * chore: update comments IRequestSerializer --- .../Builders/JsonApiApplicationBuilder.cs | 3 ++ .../JsonApiDotNetCore.csproj | 1 - .../Contracts/IUpdatedFields.cs | 1 - .../Client/IRequestSerializer.cs | 21 +++++----- .../Serialization/Client/RequestSerializer.cs | 41 ++++++------------- .../ResourceDefinitionTests.cs | 1 + .../Acceptance/TestFixture.cs | 12 +++--- test/NoEntityFrameworkTests/TestFixture.cs | 7 +++- .../Client/RequestSerializerTests.cs | 14 +++---- 9 files changed, 45 insertions(+), 56 deletions(-) diff --git a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs index 302aeb831f..8c00ce8fc1 100644 --- a/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Builders/JsonApiApplicationBuilder.cs @@ -150,6 +150,9 @@ public void ConfigureServices() _services.AddScoped(typeof(IResourceService<>), typeof(DefaultResourceService<>)); _services.AddScoped(typeof(IResourceService<,>), typeof(DefaultResourceService<,>)); + _services.AddScoped(typeof(IResourceQueryService<,>), typeof(DefaultResourceService<,>)); + _services.AddScoped(typeof(IResourceCmdService<,>), typeof(DefaultResourceService<,>)); + _services.AddSingleton(JsonApiOptions); _services.AddSingleton(resourceGraph); _services.AddSingleton(); diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj index b747656b40..7fadf92c6f 100644 --- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj @@ -27,7 +27,6 @@ -