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;
+ }
+ }
+}