diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiInputFormatter.cs b/src/JsonApiDotNetCore/Middleware/JsonApiInputFormatter.cs index fc5a1e2230..e581cbd179 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiInputFormatter.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiInputFormatter.cs @@ -1,12 +1,17 @@ +using System; +using System.Collections; +using System.Collections.Generic; using System.Threading.Tasks; +using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; namespace JsonApiDotNetCore.Middleware { - /// - public sealed class JsonApiInputFormatter : IJsonApiInputFormatter + public sealed class JsonApiInputFormatter : IJsonApiInputFormatter, IApiRequestFormatMetadataProvider { /// public bool CanRead(InputFormatterContext context) @@ -24,5 +29,43 @@ public async Task ReadAsync(InputFormatterContext context) var reader = context.HttpContext.RequestServices.GetRequiredService(); return await reader.ReadAsync(context); } + + /// + public IReadOnlyList GetSupportedContentTypes(string contentType, Type objectType) + { + ArgumentGuard.NotNull(objectType, nameof(objectType)); + + var mediaTypes = new MediaTypeCollection(); + + switch (contentType) + { + case HeaderConstants.AtomicOperationsMediaType when IsOperationsType(objectType): + { + mediaTypes.Add(MediaTypeHeaderValue.Parse(HeaderConstants.AtomicOperationsMediaType)); + break; + } + case HeaderConstants.MediaType when IsJsonApiResource(objectType): + { + mediaTypes.Add(MediaTypeHeaderValue.Parse(HeaderConstants.MediaType)); + break; + } + } + + return mediaTypes; + } + + private bool IsJsonApiResource(Type type) + { + Type typeToCheck = typeof(IEnumerable).IsAssignableFrom(type) ? type.GetGenericArguments()[0] : type; + + return typeToCheck.IsOrImplementsInterface(typeof(IIdentifiable)) || typeToCheck == typeof(object); + } + + private bool IsOperationsType(Type type) + { + Type typeToCheck = typeof(IEnumerable).IsAssignableFrom(type) ? type.GetGenericArguments()[0] : type; + + return typeToCheck == typeof(OperationContainer); + } } } diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiOutputFormatter.cs b/src/JsonApiDotNetCore/Middleware/JsonApiOutputFormatter.cs index bd66f66067..789c0644eb 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiOutputFormatter.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiOutputFormatter.cs @@ -1,12 +1,17 @@ +using System; +using System.Collections; +using System.Collections.Generic; using System.Threading.Tasks; +using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; namespace JsonApiDotNetCore.Middleware { - /// - public sealed class JsonApiOutputFormatter : IJsonApiOutputFormatter + public sealed class JsonApiOutputFormatter : IJsonApiOutputFormatter, IApiResponseTypeMetadataProvider { /// public bool CanWriteResult(OutputFormatterCanWriteContext context) @@ -24,5 +29,25 @@ public async Task WriteAsync(OutputFormatterWriteContext context) var writer = context.HttpContext.RequestServices.GetRequiredService(); await writer.WriteAsync(context); } + + /// + public IReadOnlyList GetSupportedContentTypes(string contentType, Type objectType) + { + ArgumentGuard.NotNull(objectType, nameof(objectType)); + + var mediaTypes = new MediaTypeCollection(); + + if (contentType == HeaderConstants.MediaType) + { + Type typeToCheck = typeof(IEnumerable).IsAssignableFrom(objectType) ? objectType.GetGenericArguments()[0] : objectType; + + if (typeToCheck.IsOrImplementsInterface(typeof(IIdentifiable)) || typeToCheck == typeof(object)) + { + mediaTypes.Add(MediaTypeHeaderValue.Parse(contentType)); + } + } + + return mediaTypes; + } } } diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs b/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs index 80e2aa5dce..d5ec6c4ca9 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs @@ -33,7 +33,7 @@ public class JsonApiRoutingConvention : IJsonApiRoutingConvention { private readonly IJsonApiOptions _options; private readonly IResourceContextProvider _resourceContextProvider; - private readonly HashSet _registeredTemplates = new HashSet(); + private readonly Dictionary _registeredTemplates = new Dictionary(); private readonly Dictionary _resourceContextPerControllerTypeMap = new Dictionary(); public JsonApiRoutingConvention(IJsonApiOptions options, IResourceContextProvider resourceContextProvider) @@ -89,11 +89,14 @@ public void Apply(ApplicationModel application) string template = TemplateFromResource(controller) ?? TemplateFromController(controller); - if (template == null) + if (_registeredTemplates.ContainsKey(template)) { - throw new InvalidConfigurationException($"Controllers with overlapping route templates detected: {controller.ControllerType.FullName}"); + throw new InvalidConfigurationException( + $"Cannot register '{controller.ControllerType.FullName}' for template '{template}' because '{_registeredTemplates[template].ControllerType.FullName}' was already registered for this template."); } + _registeredTemplates.Add(template, controller); + controller.Selectors[0].AttributeRouteModel = new AttributeRouteModel { Template = template @@ -116,10 +119,7 @@ private string TemplateFromResource(ControllerModel model) { string template = $"{_options.Namespace}/{resourceContext.PublicName}"; - if (_registeredTemplates.Add(template)) - { - return template; - } + return template; } return null; @@ -133,12 +133,7 @@ private string TemplateFromController(ControllerModel model) string controllerName = _options.SerializerNamingStrategy.GetPropertyName(model.ControllerName, false); string template = $"{_options.Namespace}/{controllerName}"; - if (_registeredTemplates.Add(template)) - { - return template; - } - - return null; + return template; } /// diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ApiExplorerConvention.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ApiExplorerConvention.cs new file mode 100644 index 0000000000..405c8559ed --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ApiExplorerConvention.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc.ApplicationModels; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ApiRequestFormatMedataProvider +{ + internal sealed class ApiExplorerConvention : IControllerModelConvention + { + public void Apply(ControllerModel controller) + { + controller.ApiExplorer.IsVisible = true; + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ApiExplorerStartup.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ApiExplorerStartup.cs new file mode 100644 index 0000000000..efd4137453 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ApiExplorerStartup.cs @@ -0,0 +1,23 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCoreExampleTests.Startups; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ApiRequestFormatMedataProvider +{ + [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] + public sealed class ApiExplorerStartup : TestableStartup + where TDbContext : DbContext + { + public override void ConfigureServices(IServiceCollection services) + { + IMvcCoreBuilder builder = services.AddMvcCore().AddApiExplorer(); + builder.AddMvcOptions(options => options.Conventions.Add(new ApiExplorerConvention())); + + services.UseControllersFromNamespace(GetType().Namespace); + + services.AddJsonApi(SetJsonApiOptions, mvcBuilder: builder); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ApiRequestFormatMedataProviderTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ApiRequestFormatMedataProviderTests.cs new file mode 100644 index 0000000000..1745d51c81 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ApiRequestFormatMedataProviderTests.cs @@ -0,0 +1,161 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using FluentAssertions; +using FluentAssertions.Common; +using JsonApiDotNetCore.Middleware; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ApiRequestFormatMedataProvider +{ + public sealed class ApiRequestFormatMedataProviderTests : IClassFixture, ShopDbContext>> + { + private readonly ExampleIntegrationTestContext, ShopDbContext> _testContext; + + public ApiRequestFormatMedataProviderTests(ExampleIntegrationTestContext, ShopDbContext> testContext) + { + _testContext = testContext; + } + + [Fact] + public void Can_retrieve_request_content_type_in_ApiExplorer_when_using_ConsumesAttribute() + { + // Arrange + var provider = _testContext.Factory.Services.GetRequiredService(); + + // Act + IReadOnlyList groups = provider.ApiDescriptionGroups.Items; + + // Assert + List descriptions = groups.Single().Items.ToList(); + MethodInfo postStore = typeof(StoresController).GetMethod(nameof(StoresController.PostAsync)); + + ApiDescription postStoreDescription = descriptions.First(description => (description.ActionDescriptor as ControllerActionDescriptor)?.MethodInfo == + postStore); + + postStoreDescription.Should().NotBeNull(); + postStoreDescription.SupportedRequestFormats.Should().HaveCount(1); + postStoreDescription.SupportedRequestFormats[0].MediaType.Should().Be(HeaderConstants.MediaType); + } + + [Fact] + public void Can_retrieve_atomic_operations_request_content_type_in_ApiExplorer_when_using_ConsumesAttribute() + { + // Arrange + var provider = _testContext.Factory.Services.GetRequiredService(); + + // Act + IReadOnlyList groups = provider.ApiDescriptionGroups.Items; + + // Assert + List descriptions = groups.Single().Items.ToList(); + MethodInfo postOperations = typeof(OperationsController).GetMethod(nameof(OperationsController.PostOperationsAsync)); + + ApiDescription postOperationsDescription = + descriptions.First(description => (description.ActionDescriptor as ControllerActionDescriptor)?.MethodInfo == postOperations); + + postOperationsDescription.Should().NotBeNull(); + postOperationsDescription.SupportedRequestFormats.Should().HaveCount(1); + postOperationsDescription.SupportedRequestFormats[0].MediaType.Should().Be(HeaderConstants.AtomicOperationsMediaType); + } + + [Fact] + public void Cannot_retrieve_request_content_type_in_ApiExplorer_without_usage_of_ConsumesAttribute() + { + // Arrange + var provider = _testContext.Factory.Services.GetRequiredService(); + + // Act + IReadOnlyList groups = provider.ApiDescriptionGroups.Items; + + // Assert + IReadOnlyList descriptions = groups.Single().Items; + + IEnumerable productActionDescriptions = descriptions.Where(description => + (description.ActionDescriptor as ControllerActionDescriptor)?.ControllerTypeInfo == typeof(ProductsController)); + + foreach (ApiDescription description in productActionDescriptions) + { + description.SupportedRequestFormats.Should().NotContain(format => format.MediaType == HeaderConstants.MediaType); + } + } + + [Fact] + public void Cannot_retrieve_atomic_operations_request_content_type_in_ApiExplorer_when_set_on_relationship_endpoint() + { + // Arrange + var provider = _testContext.Factory.Services.GetRequiredService(); + + // Act + IReadOnlyList groups = provider.ApiDescriptionGroups.Items; + + // Assert + List descriptions = groups.Single().Items.ToList(); + MethodInfo postRelationship = typeof(StoresController).GetMethod(nameof(StoresController.PostRelationshipAsync)); + + ApiDescription postRelationshipDescription = descriptions.First(description => + (description.ActionDescriptor as ControllerActionDescriptor)?.MethodInfo == postRelationship); + + postRelationshipDescription.Should().NotBeNull(); + postRelationshipDescription.SupportedRequestFormats.Should().HaveCount(0); + } + + [Fact] + public void Can_retrieve_response_content_type_in_ApiExplorer_when_using_ProducesAttribute_with_ProducesResponseTypeAttribute() + { + // Arrange + var provider = _testContext.Factory.Services.GetRequiredService(); + + // Act + IReadOnlyList groups = provider.ApiDescriptionGroups.Items; + + // Assert + List descriptions = groups.Single().Items.ToList(); + + MethodInfo getStores = typeof(StoresController).GetMethods() + .First(method => method.Name == nameof(StoresController.GetAsync) && method.GetParameters().Length == 1); + + ApiDescription getStoresDescription = descriptions.First(description => (description.ActionDescriptor as ControllerActionDescriptor)?.MethodInfo == + getStores); + + getStoresDescription.Should().NotBeNull(); + getStoresDescription.SupportedResponseTypes.Should().HaveCount(1); + + ApiResponseFormat jsonApiResponse = getStoresDescription.SupportedResponseTypes[0].ApiResponseFormats + .FirstOrDefault(format => format.Formatter.GetType().Implements(typeof(IJsonApiOutputFormatter))); + + jsonApiResponse.Should().NotBeNull(); + jsonApiResponse!.MediaType.Should().Be(HeaderConstants.MediaType); + } + + [Fact] + public void Cannot_retrieve_response_content_type_in_ApiExplorer_when_using_ProducesResponseTypeAttribute_without_ProducesAttribute() + { + // Arrange + var provider = _testContext.Factory.Services.GetRequiredService(); + + // Act + IReadOnlyList groups = provider.ApiDescriptionGroups.Items; + + // Assert + List descriptions = groups.Single().Items.ToList(); + + MethodInfo getStores = typeof(StoresController).GetMethods() + .First(method => method.Name == nameof(StoresController.GetAsync) && method.GetParameters().Length == 2); + + ApiDescription getStoresDescription = descriptions.First(description => (description.ActionDescriptor as ControllerActionDescriptor)?.MethodInfo == + getStores); + + getStoresDescription.Should().NotBeNull(); + getStoresDescription.SupportedResponseTypes.Should().HaveCount(1); + + ApiResponseFormat jsonApiResponse = getStoresDescription.SupportedResponseTypes[0].ApiResponseFormats + .FirstOrDefault(format => format.Formatter.GetType().Implements(typeof(IJsonApiOutputFormatter))); + + jsonApiResponse.Should().BeNull(); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/OperationsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/OperationsController.cs new file mode 100644 index 0000000000..4053064fb2 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/OperationsController.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using JsonApiDotNetCore.AtomicOperations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Resources; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ApiRequestFormatMedataProvider +{ + public sealed class OperationsController : JsonApiOperationsController + { + public OperationsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, + ITargetedFields targetedFields) + : base(options, loggerFactory, processor, request, targetedFields) + { + } + + [HttpPost] + [Consumes("application/vnd.api+json; ext=\"https://jsonapi.org/ext/atomic\"")] + public override Task PostOperationsAsync(IList operations, CancellationToken cancellationToken) + { + return base.PostOperationsAsync(operations, cancellationToken); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/Product.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/Product.cs new file mode 100644 index 0000000000..0845f61403 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/Product.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ApiRequestFormatMedataProvider +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + public sealed class Product : Identifiable + { + [Attr] + public string Name { get; set; } + + [Attr] + public decimal Price { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ProductsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ProductsController.cs new file mode 100644 index 0000000000..b5745d3544 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ProductsController.cs @@ -0,0 +1,15 @@ +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ApiRequestFormatMedataProvider +{ + public sealed class ProductsController : JsonApiController + { + public ProductsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ShopDbContext.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ShopDbContext.cs new file mode 100644 index 0000000000..004712222d --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/ShopDbContext.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ApiRequestFormatMedataProvider +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + public sealed class ShopDbContext : DbContext + { + public DbSet Stores { get; set; } + + public DbSet Products { get; set; } + + public ShopDbContext(DbContextOptions options) + : base(options) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/Store.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/Store.cs new file mode 100644 index 0000000000..e17283a920 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/Store.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ApiRequestFormatMedataProvider +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + public sealed class Store : Identifiable + { + [Attr] + public string Name { get; set; } + + [Attr] + public string Address { get; set; } + + [HasMany] + public ICollection Products { get; set; } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/StoresController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/StoresController.cs new file mode 100644 index 0000000000..708311378d --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ApiRequestFormatMedataProvider/StoresController.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ApiRequestFormatMedataProvider +{ + public sealed class StoresController : JsonApiController + { + public StoresController(IJsonApiOptions options, ILoggerFactory loggerFactory, IResourceService resourceService) + : base(options, loggerFactory, resourceService) + { + } + + [HttpGet] + [Produces("application/vnd.api+json")] + [ProducesResponseType(typeof(IEnumerable), 200)] + public override Task GetAsync(CancellationToken cancellationToken) + { + return base.GetAsync(cancellationToken); + } + + [HttpGet] + [ProducesResponseType(typeof(Store), 200)] + public override Task GetAsync(int id, CancellationToken cancellationToken) + { + return base.GetAsync(id, cancellationToken); + } + + [HttpPost] + [Consumes("application/vnd.api+json")] + public override async Task PostAsync([FromBody] Store resource, CancellationToken cancellationToken) + { + return await base.PostAsync(resource, cancellationToken); + } + + [HttpPost] + [Consumes("application/vnd.api+json; ext=\"https://jsonapi.org/ext/atomic\"")] + public override Task PostRelationshipAsync(int id, string relationshipName, ISet secondaryResourceIds, + CancellationToken cancellationToken) + { + return base.PostRelationshipAsync(id, relationshipName, secondaryResourceIds, cancellationToken); + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Controllers/AtomicConstrainedOperationsControllerTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Controllers/AtomicConstrainedOperationsControllerTests.cs index ba68a661b0..301d248362 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Controllers/AtomicConstrainedOperationsControllerTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Controllers/AtomicConstrainedOperationsControllerTests.cs @@ -18,8 +18,6 @@ public sealed class AtomicConstrainedOperationsControllerTests public AtomicConstrainedOperationsControllerTests(ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Controllers/OperationsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Controllers/OperationsController.cs new file mode 100644 index 0000000000..8812dce74e --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Controllers/OperationsController.cs @@ -0,0 +1,18 @@ +using JsonApiDotNetCore.AtomicOperations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Resources; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.AtomicOperations.Controllers +{ + public sealed class OperationsController : JsonApiOperationsController + { + public OperationsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, + ITargetedFields targetedFields) + : base(options, loggerFactory, processor, request, targetedFields) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs index af4fd37576..d8f3f74adb 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs @@ -22,8 +22,6 @@ public sealed class AtomicCreateResourceTests : IClassFixture, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs index 0bb4edb8e0..1d78d494ea 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs @@ -23,8 +23,6 @@ public AtomicCreateResourceWithClientGeneratedIdTests( { _testContext = testContext; - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); - var options = (JsonApiOptions)testContext.Factory.Services.GetRequiredService(); options.AllowClientGeneratedIds = true; } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs index dd6a0e90c6..9c6a68e202 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs @@ -22,8 +22,6 @@ public AtomicCreateResourceWithToManyRelationshipTests( ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs index 21b1aff71b..bb7f4de3a1 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs @@ -24,8 +24,6 @@ public AtomicCreateResourceWithToOneRelationshipTests( ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs index ffb3342364..6fd6f313e4 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs @@ -21,8 +21,6 @@ public sealed class AtomicDeleteResourceTests : IClassFixture, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Links/AtomicAbsoluteLinksTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Links/AtomicAbsoluteLinksTests.cs index 15ebcab128..be50c61c05 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Links/AtomicAbsoluteLinksTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Links/AtomicAbsoluteLinksTests.cs @@ -24,8 +24,6 @@ public AtomicAbsoluteLinksTests(ExampleIntegrationTestContext { - services.AddControllersFromExampleProject(); - services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>)); }); } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Links/AtomicRelativeLinksWithNamespaceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Links/AtomicRelativeLinksWithNamespaceTests.cs index 3fea795b67..f2f2194ebd 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Links/AtomicRelativeLinksWithNamespaceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Links/AtomicRelativeLinksWithNamespaceTests.cs @@ -24,8 +24,6 @@ public AtomicRelativeLinksWithNamespaceTests( testContext.ConfigureServicesAfterStartup(services => { - services.AddControllersFromExampleProject(); - services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>)); }); } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs index af3c5ab285..bf898c1991 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs @@ -20,8 +20,6 @@ public sealed class AtomicLocalIdTests : IClassFixture, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs index a0df52450c..0ebe607bba 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs @@ -24,8 +24,6 @@ public AtomicResourceMetaTests(ExampleIntegrationTestContext { - services.AddControllersFromExampleProject(); - services.AddScoped, MusicTrackMetaDefinition>(); services.AddScoped, TextLanguageMetaDefinition>(); }); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Meta/AtomicResponseMetaTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Meta/AtomicResponseMetaTests.cs index ae24a50a4a..bde6931f28 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Meta/AtomicResponseMetaTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Meta/AtomicResponseMetaTests.cs @@ -25,8 +25,6 @@ public AtomicResponseMetaTests(ExampleIntegrationTestContext { - services.AddControllersFromExampleProject(); - services.AddSingleton(); }); } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Mixed/AtomicRequestBodyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Mixed/AtomicRequestBodyTests.cs index 7e9453fcd0..ca2616c973 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Mixed/AtomicRequestBodyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Mixed/AtomicRequestBodyTests.cs @@ -18,8 +18,6 @@ public sealed class AtomicRequestBodyTests : IClassFixture, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs index 84c9f239b7..0f3253b4b7 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs @@ -20,8 +20,6 @@ public sealed class MaximumOperationsPerRequestTests public MaximumOperationsPerRequestTests(ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/ModelStateValidation/AtomicModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/ModelStateValidation/AtomicModelStateValidationTests.cs index 24d3f851dc..539732a088 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/ModelStateValidation/AtomicModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/ModelStateValidation/AtomicModelStateValidationTests.cs @@ -19,8 +19,6 @@ public sealed class AtomicModelStateValidationTests public AtomicModelStateValidationTests(ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/QueryStrings/AtomicQueryStringTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/QueryStrings/AtomicQueryStringTests.cs index 2e41befd2e..35d57e9908 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/QueryStrings/AtomicQueryStringTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/QueryStrings/AtomicQueryStringTests.cs @@ -29,8 +29,6 @@ public AtomicQueryStringTests(ExampleIntegrationTestContext { - services.AddControllersFromExampleProject(); - services.AddSingleton(new FrozenSystemClock { UtcNow = FrozenTime diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/ResourceDefinitions/AtomicSparseFieldSetResourceDefinitionTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/ResourceDefinitions/AtomicSparseFieldSetResourceDefinitionTests.cs index de4c1ecaaf..7e440326e2 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/ResourceDefinitions/AtomicSparseFieldSetResourceDefinitionTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/ResourceDefinitions/AtomicSparseFieldSetResourceDefinitionTests.cs @@ -24,8 +24,6 @@ public AtomicSparseFieldSetResourceDefinitionTests(ExampleIntegrationTestContext testContext.ConfigureServicesAfterStartup(services => { - services.AddControllersFromExampleProject(); - services.AddSingleton(); services.AddScoped, LyricTextDefinition>(); services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>)); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs index e3ff40fb65..7f1b81ec20 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs @@ -20,8 +20,6 @@ public sealed class AtomicRollbackTests : IClassFixture, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs index 899ee2510d..8bdf86bdd2 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs @@ -24,8 +24,6 @@ public AtomicTransactionConsistencyTests(ExampleIntegrationTestContext { - services.AddControllersFromExampleProject(); - services.AddResourceRepository(); services.AddResourceRepository(); services.AddResourceRepository(); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs index 87c407cd41..847190da26 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs @@ -22,8 +22,6 @@ public sealed class AtomicAddToToManyRelationshipTests public AtomicAddToToManyRelationshipTests(ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs index 4ba1b44634..bf42f9c949 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs @@ -22,8 +22,6 @@ public sealed class AtomicRemoveFromToManyRelationshipTests public AtomicRemoveFromToManyRelationshipTests(ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs index 9eca84249c..74ad1e556f 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs @@ -22,8 +22,6 @@ public sealed class AtomicReplaceToManyRelationshipTests public AtomicReplaceToManyRelationshipTests(ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs index 54365fd4da..e8d76dd6a7 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs @@ -21,8 +21,6 @@ public sealed class AtomicUpdateToOneRelationshipTests public AtomicUpdateToOneRelationshipTests(ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs index b356585dd2..cf5ddb0110 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs @@ -22,8 +22,6 @@ public sealed class AtomicReplaceToManyRelationshipTests public AtomicReplaceToManyRelationshipTests(ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs index b870ca48b3..1aefd8bda9 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs @@ -22,8 +22,6 @@ public sealed class AtomicUpdateResourceTests : IClassFixture, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs index 52f58e2506..c4fbc2d27f 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs @@ -21,8 +21,6 @@ public sealed class AtomicUpdateToOneRelationshipTests public AtomicUpdateToOneRelationshipTests(ExampleIntegrationTestContext, OperationsDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs index 25ad63189e..f59423e368 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/AcceptHeaderTests.cs @@ -18,8 +18,6 @@ public sealed class AcceptHeaderTests : IClassFixture, PolicyDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs index 3aba63977a..bb231df4e4 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/ContentTypeHeaderTests.cs @@ -17,8 +17,6 @@ public sealed class ContentTypeHeaderTests : IClassFixture, PolicyDbContext> testContext) { _testContext = testContext; - - testContext.ConfigureServicesAfterStartup(services => services.AddControllersFromExampleProject()); } [Fact] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/OperationsController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/OperationsController.cs new file mode 100644 index 0000000000..dc89d1811d --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ContentNegotiation/OperationsController.cs @@ -0,0 +1,18 @@ +using JsonApiDotNetCore.AtomicOperations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Resources; +using Microsoft.Extensions.Logging; + +namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ContentNegotiation +{ + public sealed class OperationsController : JsonApiOperationsController + { + public OperationsController(IJsonApiOptions options, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, + ITargetedFields targetedFields) + : base(options, loggerFactory, processor, request, targetedFields) + { + } + } +} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs index 57974b5ede..34415e7840 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ResourceHooks/ResourceHooksStartup.cs @@ -1,6 +1,9 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCoreExample.Controllers; +using JsonApiDotNetCoreExample.Startups; using JsonApiDotNetCoreExampleTests.Startups; +using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; @@ -12,10 +15,10 @@ public sealed class ResourceHooksStartup : TestableStartup(SetJsonApiOptions); } protected override void SetJsonApiOptions(JsonApiOptions options) diff --git a/test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs b/test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs index b0ec80ebe3..3a62eabe57 100644 --- a/test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs +++ b/test/JsonApiDotNetCoreExampleTests/ServiceCollectionExtensions.cs @@ -1,15 +1,29 @@ -using JsonApiDotNetCoreExample.Startups; +using System.Linq; +using JsonApiDotNetCore; using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; namespace JsonApiDotNetCoreExampleTests { internal static class ServiceCollectionExtensions { - public static void AddControllersFromExampleProject(this IServiceCollection services) + public static void UseControllersFromNamespace(this IServiceCollection services, string @namespace, AssemblyPart assemblyWithNamespace = null) { - var part = new AssemblyPart(typeof(EmptyStartup).Assembly); - services.AddMvcCore().ConfigureApplicationPartManager(apm => apm.ApplicationParts.Add(part)); + ArgumentGuard.NotNull(@namespace, nameof(@namespace)); + + services.AddMvcCore().ConfigureApplicationPartManager(manager => + { + if (assemblyWithNamespace != null) + { + manager.ApplicationParts.Add(assemblyWithNamespace); + } + + ControllerFeatureProvider provider = manager.FeatureProviders.OfType().First(); + manager.FeatureProviders.Remove(provider); + + manager.FeatureProviders.Add(new TestControllerProvider(@namespace)); + }); } } } diff --git a/test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs b/test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs index a486046b4e..c6608032e8 100644 --- a/test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs +++ b/test/JsonApiDotNetCoreExampleTests/Startups/TestableStartup.cs @@ -14,6 +14,8 @@ public class TestableStartup : EmptyStartup { public override void ConfigureServices(IServiceCollection services) { + services.UseControllersFromNamespace(typeof(TDbContext).Namespace); + services.AddJsonApi(SetJsonApiOptions); } diff --git a/test/JsonApiDotNetCoreExampleTests/TestControllerProvider.cs b/test/JsonApiDotNetCoreExampleTests/TestControllerProvider.cs new file mode 100644 index 0000000000..0119f4d807 --- /dev/null +++ b/test/JsonApiDotNetCoreExampleTests/TestControllerProvider.cs @@ -0,0 +1,27 @@ +using System; +using System.Reflection; +using JsonApiDotNetCore; +using Microsoft.AspNetCore.Mvc.Controllers; + +namespace JsonApiDotNetCoreExampleTests +{ + internal sealed class TestControllerProvider : ControllerFeatureProvider + { + private readonly string _controllerNamespace; + + public TestControllerProvider(string controllerNamespace) + { + ArgumentGuard.NotNull(controllerNamespace, nameof(controllerNamespace)); + + _controllerNamespace = controllerNamespace; + } + + protected override bool IsController(TypeInfo typeInfo) + { + bool isController = base.IsController(typeInfo); + + bool controllerInAllowedNamespace = isController && typeInfo.Namespace!.StartsWith(_controllerNamespace, StringComparison.Ordinal); + return controllerInAllowedNamespace; + } + } +}