diff --git a/Build.ps1 b/Build.ps1
index b65fb523..e2e638fa 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -8,7 +8,8 @@ function CheckLastExitCode {
function RunInspectCode {
$outputPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'jetbrains-inspectcode-results.xml')
- dotnet jb inspectcode JsonApiDotNetCore.sln --no-build --output="$outputPath" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=GlobalPerProduct -dsl=SolutionPersonal -dsl=ProjectPersonal
+ # passing --build instead of --no-build as workaround for https://youtrack.jetbrains.com/issue/RSRP-487054
+ dotnet jb inspectcode JsonApiDotNetCore.sln --build --output="$outputPath" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=GlobalPerProduct -dsl=SolutionPersonal -dsl=ProjectPersonal
CheckLastExitCode
[xml]$xml = Get-Content "$outputPath"
@@ -84,10 +85,10 @@ function CreateNuGetPackage {
}
if ([string]::IsNullOrWhitespace($versionSuffix)) {
- dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts
+ dotnet pack --no-restore --no-build --configuration Release --output .\artifacts
}
else {
- dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=$versionSuffix
+ dotnet pack --no-restore --no-build --configuration Release --output .\artifacts --version-suffix=$versionSuffix
}
CheckLastExitCode
diff --git a/Directory.Build.props b/Directory.Build.props
index 895d25aa..da45eb94 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,9 +4,14 @@
5.0.*
5.0.*
5.0.*
+ 3.*
+ 2.11.10
+ 5.0.0
$(MSBuildThisFileDirectory)CodingGuidelines.ruleset
9999
enable
+ false
+ false
diff --git a/JsonApiDotNetCore.sln b/JsonApiDotNetCore.sln
index e5e97193..ade710c4 100644
--- a/JsonApiDotNetCore.sln
+++ b/JsonApiDotNetCore.sln
@@ -44,6 +44,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiDbContextTests", "test
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBuildingBlocks", "test\TestBuildingBlocks\TestBuildingBlocks.csproj", "{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore.SourceGenerators", "src\JsonApiDotNetCore.SourceGenerators\JsonApiDotNetCore.SourceGenerators.csproj", "{952C0FDE-AFC8-455C-986F-6CC882ED8953}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGeneratorDebugger", "test\SourceGeneratorDebugger\SourceGeneratorDebugger.csproj", "{87D066F9-3540-4AC7-A748-134900969EE5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGeneratorTests", "test\SourceGeneratorTests\SourceGeneratorTests.csproj", "{0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -54,6 +60,18 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x64.Build.0 = Debug|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x86.Build.0 = Debug|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x64.ActiveCfg = Release|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x64.Build.0 = Release|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x86.ActiveCfg = Release|Any CPU
+ {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x86.Build.0 = Release|Any CPU
{CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CAF331F8-9255-4D72-A1A8-A54141E99F1E}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -162,18 +180,6 @@ Global
{21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|x64.Build.0 = Release|Any CPU
{21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|x86.ActiveCfg = Release|Any CPU
{21D27239-138D-4604-8E49-DCBE41BCE4C8}.Release|x86.Build.0 = Release|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x64.ActiveCfg = Debug|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x64.Build.0 = Debug|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x86.ActiveCfg = Debug|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Debug|x86.Build.0 = Debug|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|Any CPU.Build.0 = Release|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x64.ActiveCfg = Release|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x64.Build.0 = Release|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x86.ActiveCfg = Release|Any CPU
- {067FFD7A-C66B-473D-8471-37F5C95DF61C}.Release|x86.Build.0 = Release|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -210,6 +216,42 @@ Global
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x64.Build.0 = Release|Any CPU
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x86.ActiveCfg = Release|Any CPU
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21}.Release|x86.Build.0 = Release|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Debug|x64.Build.0 = Debug|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Debug|x86.Build.0 = Debug|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Release|Any CPU.Build.0 = Release|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Release|x64.ActiveCfg = Release|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Release|x64.Build.0 = Release|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Release|x86.ActiveCfg = Release|Any CPU
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953}.Release|x86.Build.0 = Release|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Debug|x64.Build.0 = Debug|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Debug|x86.Build.0 = Debug|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Release|x64.ActiveCfg = Release|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Release|x64.Build.0 = Release|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Release|x86.ActiveCfg = Release|Any CPU
+ {87D066F9-3540-4AC7-A748-134900969EE5}.Release|x86.Build.0 = Release|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Debug|x64.Build.0 = Debug|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Debug|x86.Build.0 = Debug|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Release|x64.ActiveCfg = Release|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Release|x64.Build.0 = Release|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Release|x86.ActiveCfg = Release|Any CPU
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -228,6 +270,9 @@ Global
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
{210FD61E-FF5D-4CEE-8E0D-C739ECCCBA21} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
+ {952C0FDE-AFC8-455C-986F-6CC882ED8953} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
+ {87D066F9-3540-4AC7-A748-134900969EE5} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
+ {0E0B5C51-F7E2-4F40-A4E4-DED0E9731DC9} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4}
diff --git a/README.md b/README.md
index e9670ff3..5df6ed6b 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,7 @@ See [our documentation](https://www.jsonapi.net/) for detailed usage.
```c#
#nullable enable
+[Resource]
public class Article : Identifiable
{
[Attr]
@@ -52,19 +53,6 @@ public class Article : Identifiable
}
```
-### Controllers
-
-```c#
-public class ArticlesController : JsonApiController
-{
- public ArticlesController(IJsonApiOptions options, IResourceGraph resourceGraph,
- ILoggerFactory loggerFactory, IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
-}
-```
-
### Middleware
```c#
diff --git a/ROADMAP.md b/ROADMAP.md
index 49f499e7..a1fc9267 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -24,11 +24,11 @@ The need for breaking changes has blocked several efforts in the v4.x release, s
- [x] Nullable reference types [#1029](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1029)
- [x] Improved paging links [#1010](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1010)
- [x] Configuration validation [#170](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/170)
+- [x] Auto-generated controllers [#732](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/732) [#365](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/365)
- [ ] Support .NET 6 with EF Core 6 [#1109](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1109)
Aside from the list above, we have interest in the following topics. It's too soon yet to decide whether they'll make it into v5.x or in a later major version.
-- Auto-generated controllers [#732](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/732) [#365](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/365)
- Optimistic concurrency [#1004](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1004)
- Extract annotations into separate package [#730](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/730)
- OpenAPI (Swagger) [#1046](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1046)
diff --git a/docs/getting-started/step-by-step.md b/docs/getting-started/step-by-step.md
index 21daf041..69754656 100644
--- a/docs/getting-started/step-by-step.md
+++ b/docs/getting-started/step-by-step.md
@@ -7,7 +7,6 @@ The shortest path to a running API looks like:
- Install
- Define models
- Define the DbContext
-- Define controllers
- Add Middleware and Services
- Seed the database
- Start the app
@@ -40,6 +39,7 @@ The easiest way to do this is to inherit from `Identifiable`.
```c#
#nullable enable
+[Resource]
public class Person : Identifiable
{
[Attr]
@@ -63,22 +63,6 @@ public class AppDbContext : DbContext
}
```
-### Define Controllers
-
-You need to create controllers that inherit from `JsonApiController`
-where `TResource` is the model that inherits from `Identifiable`.
-
-```c#
-public class PeopleController : JsonApiController
-{
- public PeopleController(IJsonApiOptions options, IResourceGraph resourceGraph,
- ILoggerFactory loggerFactory, IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
-}
-```
-
### Middleware and Services
Finally, add the services by adding the following to your Startup.ConfigureServices:
diff --git a/docs/usage/extensibility/controllers.md b/docs/usage/extensibility/controllers.md
index 1993f778..0c71f450 100644
--- a/docs/usage/extensibility/controllers.md
+++ b/docs/usage/extensibility/controllers.md
@@ -1,6 +1,99 @@
# Controllers
-You need to create controllers that inherit from `JsonApiController`
+To expose API endpoints, ASP.NET controllers need to be defined.
+
+_since v5_
+
+Controllers are auto-generated (using [source generators](https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview)) when you add `[Resource]` on your model class:
+
+```c#
+[Resource] // Generates ArticlesController.g.cs
+public class Article : Identifiable
+{
+ // ...
+}
+```
+
+## Resource Access Control
+
+It is often desirable to limit which endpoints are exposed on your controller.
+A subset can be specified too:
+
+```c#
+[Resource(GenerateControllerEndpoints =
+ JsonApiEndpoints.GetCollection | JsonApiEndpoints.GetSingle)]
+public class Article : Identifiable
+{
+ // ...
+}
+```
+
+Instead of passing a set of endpoints, you can use `JsonApiEndpoints.Query` to generate all read-only endpoints or `JsonApiEndpoints.Command` for all write-only endpoints.
+
+When an endpoint is blocked, an HTTP 403 Forbidden response is returned.
+
+```http
+DELETE http://localhost:14140/articles/1 HTTP/1.1
+```
+
+```json
+{
+ "links": {
+ "self": "/articles"
+ },
+ "errors": [
+ {
+ "id": "dde7f219-2274-4473-97ef-baac3e7c1487",
+ "status": "403",
+ "title": "The requested endpoint is not accessible.",
+ "detail": "Endpoint '/articles/1' is not accessible for DELETE requests."
+ }
+ ]
+}
+```
+
+## Augmenting controllers
+
+Auto-generated controllers can easily be augmented because they are partial classes. For example:
+
+```c#
+[DisableRoutingConvention]
+[Route("some/custom/route")]
+[DisableQueryString(JsonApiQueryStringParameters.Include)]
+partial class ArticlesController
+{
+ [HttpPost]
+ public IActionResult Upload()
+ {
+ // ...
+ }
+}
+```
+
+If you need to inject extra dependencies, tell the IoC container with `[ActivatorUtilitiesConstructor]` to prefer your constructor:
+
+```c#
+partial class ArticlesController
+{
+ private IAuthenticationService _authService;
+
+ [ActivatorUtilitiesConstructor]
+ public ArticlesController(IAuthenticationService authService, IJsonApiOptions options,
+ IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
+ IResourceService resourceService)
+ : base(options, resourceGraph, loggerFactory, resourceService)
+ {
+ _authService = authService;
+ }
+}
+```
+
+In case you don't want to use auto-generated controllers and define them yourself (see below), remove
+`[Resource]` from your models or use `[Resource(GenerateControllerEndpoints = JsonApiEndpoints.None)]`.
+
+## Earlier versions
+
+In earlier versions of JsonApiDotNetCore, you needed to create controllers that inherit from `JsonApiController`. For example:
```c#
public class ArticlesController : JsonApiController
@@ -15,7 +108,7 @@ public class ArticlesController : JsonApiController
If you want to setup routes yourself, you can instead inherit from `BaseJsonApiController` and override its methods with your own `[HttpGet]`, `[HttpHead]`, `[HttpPost]`, `[HttpPatch]` and `[HttpDelete]` attributes added on them. Don't forget to add `[FromBody]` on parameters where needed.
-## Resource Access Control
+### Resource Access Control
It is often desirable to limit which routes are exposed on your controller.
@@ -37,25 +130,3 @@ public class ReportsController : JsonApiController
```
For more information about resource service injection, see [Replacing injected services](~/usage/extensibility/layer-overview.md#replacing-injected-services) and [Resource Services](~/usage/extensibility/services.md).
-
-When a route is blocked, an HTTP 403 Forbidden response is returned.
-
-```http
-DELETE http://localhost:14140/people/1 HTTP/1.1
-```
-
-```json
-{
- "links": {
- "self": "/api/v1/people"
- },
- "errors": [
- {
- "id": "dde7f219-2274-4473-97ef-baac3e7c1487",
- "status": "403",
- "title": "The requested endpoint is not accessible.",
- "detail": "Endpoint '/people/1' is not accessible for DELETE requests."
- }
- ]
-}
-```
diff --git a/docs/usage/extensibility/services.md b/docs/usage/extensibility/services.md
index 77d77243..5270f4bb 100644
--- a/docs/usage/extensibility/services.md
+++ b/docs/usage/extensibility/services.md
@@ -1,7 +1,8 @@
# Resource Services
The `IResourceService` acts as a service layer between the controller and the data access layer.
-This allows you to customize it however you want. This is also a good place to implement custom business logic.
+This allows you to customize it however you want. While this is still a potential place to implement custom business logic,
+since v4, [Resource Definitions](~/usage/extensibility/resource-definitions.md) are more suitable for that.
## Supplementing Default Behavior
@@ -77,7 +78,7 @@ public class ProductService : IResourceService
## Limited Requirements
-In some cases it may be necessary to only expose a few methods on a resource. For this reason, we have created a hierarchy of service interfaces that can be used to get the exact implementation you require.
+In some cases it may be necessary to only expose a few actions on a resource. For this reason, we have created a hierarchy of service interfaces that can be used to get the exact implementation you require.
This interface hierarchy is defined by this tree structure.
@@ -152,7 +153,18 @@ public class Startup
}
```
-Then in the controller, you should inherit from the JSON:API controller and pass the services into the named, optional base parameters:
+Then on your model, pass in the set of endpoints to expose (the ones that you've registered services for):
+
+```c#
+[Resource(GenerateControllerEndpoints =
+ JsonApiEndpoints.Create | JsonApiEndpoints.Delete)]
+public class Article : Identifiable
+{
+ // ...
+}
+```
+
+Alternatively, when using a hand-written controller, you should inherit from the JSON:API controller and pass the services into the named, optional base parameters:
```c#
public class ArticlesController : JsonApiController
diff --git a/docs/usage/resource-graph.md b/docs/usage/resource-graph.md
index beb20d2d..4232419b 100644
--- a/docs/usage/resource-graph.md
+++ b/docs/usage/resource-graph.md
@@ -72,28 +72,28 @@ public void ConfigureServices(IServiceCollection services)
## Resource Name
-The public resource name is exposed through the `type` member in the JSON:API payload. This can be configured by the following approaches (in order of priority):
+The public resource name is exposed through the `type` member in the JSON:API payload. This can be configured using the following approaches (in order of priority):
-1. The `publicName` parameter when manually adding a resource to the graph
+1. The `publicName` parameter when manually adding a resource to the graph.
```c#
services.AddJsonApi(resources: builder =>
{
- builder.Add(publicName: "people");
+ builder.Add(publicName: "individuals");
});
```
-2. The model is decorated with a `ResourceAttribute`
+2. The `PublicName` property when a model is decorated with a `ResourceAttribute`.
```c#
-[Resource("myResources")]
-public class MyModel : Identifiable
+[Resource(PublicName = "individuals")]
+public class Person : Identifiable
{
}
```
-3. The configured naming convention (by default this is camel-case).
+3. The configured naming convention (by default this is camel-case), after pluralization.
```c#
-// this will be registered as "myModels"
-public class MyModel : Identifiable
+// this will be registered as "people"
+public class Person : Identifiable
{
}
```
diff --git a/docs/usage/routing.md b/docs/usage/routing.md
index c68914a0..c4eb8ae0 100644
--- a/docs/usage/routing.md
+++ b/docs/usage/routing.md
@@ -6,7 +6,7 @@ An endpoint URL provides access to a resource or a relationship. Resource endpoi
In the relationship endpoint "/articles/1/relationships/comments", "articles" is the left side of the relationship and "comments" the right side.
-## Namespacing and Versioning URLs
+## Namespacing and versioning of URLs
You can add a namespace to all URLs by specifying it in ConfigureServices.
```c#
@@ -18,15 +18,18 @@ public void ConfigureServices(IServiceCollection services)
Which results in URLs like: https://yourdomain.com/api/v1/people
-## Default Routing Convention
+## Default routing convention
-The library will configure routes for all controllers in your project. By default, routes are camel-cased. This is based on the [recommendations](https://jsonapi.org/recommendations/) outlined in the JSON:API spec.
+The library will configure routes for all auto-generated and hand-written controllers in your project. By default, routes are camel-cased. This is based on the [recommendations](https://jsonapi.org/recommendations/) outlined in the JSON:API spec.
```c#
-public class OrderLine : Identifiable
+// Auto-generated
+[Resource]
+public class OrderSummary : Identifiable
{
}
+// Hand-written
public class OrderLineController : JsonApiController
{
public OrderLineController(IJsonApiOptions options, IResourceGraph resourceGraph,
@@ -38,6 +41,7 @@ public class OrderLineController : JsonApiController
```
```http
+GET /orderSummaries HTTP/1.1
GET /orderLines HTTP/1.1
```
@@ -57,12 +61,21 @@ public class OrderLineController : ControllerBase
GET /orderLines HTTP/1.1
```
-## Disabling the Default Routing Convention
+### Customized routes
-It is possible to bypass the default routing convention for a controller.
+It is possible to override the default routing convention for an auto-generated or hand-written controller.
```c#
-[Route("v1/custom/route/lines-in-order"), DisableRoutingConvention]
+// Auto-generated
+[DisableRoutingConvention]
+[Route("v1/custom/route/summaries-for-orders")]
+partial class OrderSummariesController
+{
+}
+
+// Hand-written
+[DisableRoutingConvention]
+[Route("v1/custom/route/lines-in-order")]
public class OrderLineController : JsonApiController
{
public OrderLineController(IJsonApiOptions options, IResourceGraph resourceGraph,
@@ -73,7 +86,7 @@ public class OrderLineController : JsonApiController
}
```
-## Advanced Usage: Custom Routing Convention
+## Advanced usage: custom routing convention
It is possible to replace the built-in routing convention with a [custom routing convention](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/application-model?view=aspnetcore-3.1#sample-custom-routing-convention) by registering an implementation of `IJsonApiRoutingConvention`.
diff --git a/src/Examples/GettingStarted/Controllers/BooksController.cs b/src/Examples/GettingStarted/Controllers/BooksController.cs
deleted file mode 100644
index 3f049429..00000000
--- a/src/Examples/GettingStarted/Controllers/BooksController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using GettingStarted.Models;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace GettingStarted.Controllers
-{
- public sealed class BooksController : JsonApiController
- {
- public BooksController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/src/Examples/GettingStarted/Controllers/PeopleController.cs b/src/Examples/GettingStarted/Controllers/PeopleController.cs
deleted file mode 100644
index e7a5537f..00000000
--- a/src/Examples/GettingStarted/Controllers/PeopleController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using GettingStarted.Models;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace GettingStarted.Controllers
-{
- public sealed class PeopleController : JsonApiController
- {
- public PeopleController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/src/Examples/GettingStarted/GettingStarted.csproj b/src/Examples/GettingStarted/GettingStarted.csproj
index b28d7a0c..ffffc7b2 100644
--- a/src/Examples/GettingStarted/GettingStarted.csproj
+++ b/src/Examples/GettingStarted/GettingStarted.csproj
@@ -5,6 +5,7 @@
+
diff --git a/src/Examples/GettingStarted/Models/Book.cs b/src/Examples/GettingStarted/Models/Book.cs
index 0957461c..1e1cb42b 100644
--- a/src/Examples/GettingStarted/Models/Book.cs
+++ b/src/Examples/GettingStarted/Models/Book.cs
@@ -5,6 +5,7 @@
namespace GettingStarted.Models
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource]
public sealed class Book : Identifiable
{
[Attr]
diff --git a/src/Examples/GettingStarted/Models/Person.cs b/src/Examples/GettingStarted/Models/Person.cs
index f9b8e55f..056d3b05 100644
--- a/src/Examples/GettingStarted/Models/Person.cs
+++ b/src/Examples/GettingStarted/Models/Person.cs
@@ -6,6 +6,7 @@
namespace GettingStarted.Models
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource]
public sealed class Person : Identifiable
{
[Attr]
diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/PeopleController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/PeopleController.cs
deleted file mode 100644
index 0ebafd17..00000000
--- a/src/Examples/JsonApiDotNetCoreExample/Controllers/PeopleController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using JsonApiDotNetCoreExample.Models;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreExample.Controllers
-{
- public sealed class PeopleController : JsonApiController
- {
- public PeopleController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs
deleted file mode 100644
index b08af4e3..00000000
--- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using JsonApiDotNetCoreExample.Models;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreExample.Controllers
-{
- public sealed class TagsController : JsonApiController
- {
- public TagsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs
deleted file mode 100644
index c8628533..00000000
--- a/src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using JsonApiDotNetCoreExample.Models;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreExample.Controllers
-{
- public sealed class TodoItemsController : JsonApiController
- {
- public TodoItemsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj
index 95c1faf8..8e2baedf 100644
--- a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj
+++ b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj
@@ -5,6 +5,8 @@
+
diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs
index 44be2df8..6814db7f 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreExample.Models
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource]
public sealed class Person : Identifiable
{
[Attr]
diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Tag.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Tag.cs
index 713eafe6..a620c317 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Models/Tag.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Models/Tag.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreExample.Models
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource]
public sealed class Tag : Identifiable
{
[Attr]
diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs
index 5c4d5c6e..872322b0 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs
@@ -8,6 +8,7 @@
namespace JsonApiDotNetCoreExample.Models
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource]
public sealed class TodoItem : Identifiable
{
[Attr]
diff --git a/src/Examples/MultiDbContextExample/Controllers/ResourceAsController.cs b/src/Examples/MultiDbContextExample/Controllers/ResourceAsController.cs
deleted file mode 100644
index 5fd3c662..00000000
--- a/src/Examples/MultiDbContextExample/Controllers/ResourceAsController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-using MultiDbContextExample.Models;
-
-namespace MultiDbContextExample.Controllers
-{
- public sealed class ResourceAsController : JsonApiController
- {
- public ResourceAsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/src/Examples/MultiDbContextExample/Controllers/ResourceBsController.cs b/src/Examples/MultiDbContextExample/Controllers/ResourceBsController.cs
deleted file mode 100644
index 33b89aa9..00000000
--- a/src/Examples/MultiDbContextExample/Controllers/ResourceBsController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-using MultiDbContextExample.Models;
-
-namespace MultiDbContextExample.Controllers
-{
- public sealed class ResourceBsController : JsonApiController
- {
- public ResourceBsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/src/Examples/MultiDbContextExample/Models/ResourceA.cs b/src/Examples/MultiDbContextExample/Models/ResourceA.cs
index 1c754be6..bed097b0 100644
--- a/src/Examples/MultiDbContextExample/Models/ResourceA.cs
+++ b/src/Examples/MultiDbContextExample/Models/ResourceA.cs
@@ -5,6 +5,7 @@
namespace MultiDbContextExample.Models
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource]
public sealed class ResourceA : Identifiable
{
[Attr]
diff --git a/src/Examples/MultiDbContextExample/Models/ResourceB.cs b/src/Examples/MultiDbContextExample/Models/ResourceB.cs
index 70941a1f..751b3859 100644
--- a/src/Examples/MultiDbContextExample/Models/ResourceB.cs
+++ b/src/Examples/MultiDbContextExample/Models/ResourceB.cs
@@ -5,6 +5,7 @@
namespace MultiDbContextExample.Models
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource]
public sealed class ResourceB : Identifiable
{
[Attr]
diff --git a/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj b/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj
index b28d7a0c..0216243b 100644
--- a/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj
+++ b/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj
@@ -5,6 +5,8 @@
+
diff --git a/src/Examples/NoEntityFrameworkExample/Controllers/WorkItemsController.cs b/src/Examples/NoEntityFrameworkExample/Controllers/WorkItemsController.cs
deleted file mode 100644
index 055fa60e..00000000
--- a/src/Examples/NoEntityFrameworkExample/Controllers/WorkItemsController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-using NoEntityFrameworkExample.Models;
-
-namespace NoEntityFrameworkExample.Controllers
-{
- public sealed class WorkItemsController : JsonApiController
- {
- public WorkItemsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/src/Examples/NoEntityFrameworkExample/Models/WorkItem.cs b/src/Examples/NoEntityFrameworkExample/Models/WorkItem.cs
index 083894fd..a64feb70 100644
--- a/src/Examples/NoEntityFrameworkExample/Models/WorkItem.cs
+++ b/src/Examples/NoEntityFrameworkExample/Models/WorkItem.cs
@@ -6,6 +6,7 @@
namespace NoEntityFrameworkExample.Models
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource]
public sealed class WorkItem : Identifiable
{
[Attr]
diff --git a/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj b/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj
index f7c5aa19..14ef3721 100644
--- a/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj
+++ b/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj
@@ -5,6 +5,8 @@
+
diff --git a/src/Examples/ReportsExample/Controllers/ReportsController.cs b/src/Examples/ReportsExample/Controllers/ReportsController.cs
deleted file mode 100644
index 8c177e7d..00000000
--- a/src/Examples/ReportsExample/Controllers/ReportsController.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System.Threading;
-using System.Threading.Tasks;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using ReportsExample.Models;
-
-namespace ReportsExample.Controllers
-{
- [Route("api/[controller]")]
- public class ReportsController : BaseJsonApiController
- {
- public ReportsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IGetAllService getAllService)
- : base(options, resourceGraph, loggerFactory, getAllService)
- {
- }
-
- [HttpGet]
- public override async Task GetAsync(CancellationToken cancellationToken)
- {
- return await base.GetAsync(cancellationToken);
- }
- }
-}
diff --git a/src/Examples/ReportsExample/Models/Report.cs b/src/Examples/ReportsExample/Models/Report.cs
index 65f6972d..384cc36c 100644
--- a/src/Examples/ReportsExample/Models/Report.cs
+++ b/src/Examples/ReportsExample/Models/Report.cs
@@ -1,10 +1,12 @@
using JetBrains.Annotations;
+using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;
namespace ReportsExample.Models
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(GenerateControllerEndpoints = JsonApiEndpoints.GetCollection)]
public sealed class Report : Identifiable
{
[Attr]
diff --git a/src/Examples/ReportsExample/ReportsExample.csproj b/src/Examples/ReportsExample/ReportsExample.csproj
index 95c1faf8..8e2baedf 100644
--- a/src/Examples/ReportsExample/ReportsExample.csproj
+++ b/src/Examples/ReportsExample/ReportsExample.csproj
@@ -5,6 +5,8 @@
+
diff --git a/src/JsonApiDotNetCore.SourceGenerators/ControllerSourceGenerator.cs b/src/JsonApiDotNetCore.SourceGenerators/ControllerSourceGenerator.cs
new file mode 100644
index 00000000..6728fd53
--- /dev/null
+++ b/src/JsonApiDotNetCore.SourceGenerators/ControllerSourceGenerator.cs
@@ -0,0 +1,170 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using Humanizer;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+#pragma warning disable RS2008 // Enable analyzer release tracking
+
+namespace JsonApiDotNetCore.SourceGenerators
+{
+ [Generator(LanguageNames.CSharp)]
+ public sealed class ControllerSourceGenerator : ISourceGenerator
+ {
+ private const string Category = "JsonApiDotNetCore";
+
+ private static readonly DiagnosticDescriptor MissingInterfaceWarning = new DiagnosticDescriptor("JADNC001",
+ "Resource type does not implement IIdentifiable",
+ "Type '{0}' must implement IIdentifiable when using ResourceAttribute to auto-generate ASP.NET controllers", Category,
+ DiagnosticSeverity.Warning, true);
+
+ private static readonly DiagnosticDescriptor MissingIndentInTableError = new DiagnosticDescriptor("JADNC900",
+ "Internal error: Insufficient entries in IndentTable", "Internal error: Missing entry in IndentTable for depth {0}", Category,
+ DiagnosticSeverity.Warning, true);
+
+ // PERF: Heap-allocate the delegate only once, instead of per compilation.
+ private static readonly SyntaxReceiverCreator CreateSyntaxReceiver = () => new TypeWithAttributeSyntaxReceiver();
+
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForSyntaxNotifications(CreateSyntaxReceiver);
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ var receiver = (TypeWithAttributeSyntaxReceiver)context.SyntaxReceiver;
+
+ if (receiver == null)
+ {
+ return;
+ }
+
+ INamedTypeSymbol resourceAttributeType = context.Compilation.GetTypeByMetadataName("JsonApiDotNetCore.Resources.Annotations.ResourceAttribute");
+ INamedTypeSymbol identifiableOpenInterface = context.Compilation.GetTypeByMetadataName("JsonApiDotNetCore.Resources.IIdentifiable`1");
+ INamedTypeSymbol loggerFactoryInterface = context.Compilation.GetTypeByMetadataName("Microsoft.Extensions.Logging.ILoggerFactory");
+
+ if (resourceAttributeType == null || identifiableOpenInterface == null || loggerFactoryInterface == null)
+ {
+ return;
+ }
+
+ var controllerNamesInUse = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ var writer = new SourceCodeWriter(context, MissingIndentInTableError);
+
+ foreach (TypeDeclarationSyntax typeDeclarationSyntax in receiver.TypeDeclarations)
+ {
+ // PERF: Note that our code runs on every keystroke in the IDE, which makes it critical to provide near-realtime performance.
+ // This means keeping an eye on memory allocations and bailing out early when compilations are cancelled while the user is still typing.
+ context.CancellationToken.ThrowIfCancellationRequested();
+
+ SemanticModel semanticModel = context.Compilation.GetSemanticModel(typeDeclarationSyntax.SyntaxTree);
+ INamedTypeSymbol resourceType = semanticModel.GetDeclaredSymbol(typeDeclarationSyntax, context.CancellationToken);
+
+ if (resourceType == null)
+ {
+ continue;
+ }
+
+ AttributeData resourceAttributeData = FirstOrDefault(resourceType.GetAttributes(), resourceAttributeType,
+ (data, type) => SymbolEqualityComparer.Default.Equals(data.AttributeClass, type));
+
+ if (resourceAttributeData == null)
+ {
+ continue;
+ }
+
+ TypedConstant endpointsArgument = resourceAttributeData.NamedArguments.FirstOrDefault(pair => pair.Key == "GenerateControllerEndpoints").Value;
+
+ if (endpointsArgument.Value != null && (JsonApiEndpointsCopy)endpointsArgument.Value == JsonApiEndpointsCopy.None)
+ {
+ continue;
+ }
+
+ TypedConstant controllerNamespaceArgument =
+ resourceAttributeData.NamedArguments.FirstOrDefault(pair => pair.Key == "ControllerNamespace").Value;
+
+ string controllerNamespace = GetControllerNamespace(controllerNamespaceArgument, resourceType);
+
+ INamedTypeSymbol identifiableClosedInterface = FirstOrDefault(resourceType.AllInterfaces, identifiableOpenInterface,
+ (@interface, openInterface) => @interface.IsGenericType &&
+ SymbolEqualityComparer.Default.Equals(@interface.ConstructedFrom, openInterface));
+
+ if (identifiableClosedInterface == null)
+ {
+ var diagnostic = Diagnostic.Create(MissingInterfaceWarning, typeDeclarationSyntax.GetLocation(), resourceType.Name);
+ context.ReportDiagnostic(diagnostic);
+ continue;
+ }
+
+ ITypeSymbol idType = identifiableClosedInterface.TypeArguments[0];
+ string controllerName = $"{resourceType.Name.Pluralize()}Controller";
+ JsonApiEndpointsCopy endpointsToGenerate = (JsonApiEndpointsCopy?)(int?)endpointsArgument.Value ?? JsonApiEndpointsCopy.All;
+
+ string sourceCode = writer.Write(resourceType, idType, endpointsToGenerate, controllerNamespace, controllerName, loggerFactoryInterface);
+ SourceText sourceText = SourceText.From(sourceCode, Encoding.UTF8);
+
+ string fileName = GetUniqueFileName(controllerName, controllerNamesInUse);
+ context.AddSource(fileName, sourceText);
+ }
+ }
+
+ private static TElement FirstOrDefault(ImmutableArray source, TContext context, Func predicate)
+ {
+ // PERF: Using this method enables to avoid allocating a closure in the passed lambda expression.
+ // See https://www.jetbrains.com/help/resharper/2021.2/Fixing_Issues_Found_by_DPA.html#closures-in-lambda-expressions.
+
+ foreach (TElement element in source)
+ {
+ if (predicate(element, context))
+ {
+ return element;
+ }
+ }
+
+ return default;
+ }
+
+ private static string GetControllerNamespace(TypedConstant controllerNamespaceArgument, INamedTypeSymbol resourceType)
+ {
+ if (!controllerNamespaceArgument.IsNull)
+ {
+ return (string)controllerNamespaceArgument.Value;
+ }
+
+ if (resourceType.ContainingNamespace.IsGlobalNamespace)
+ {
+ return null;
+ }
+
+ if (resourceType.ContainingNamespace.ContainingNamespace.IsGlobalNamespace)
+ {
+ return "Controllers";
+ }
+
+ return $"{resourceType.ContainingNamespace.ContainingNamespace}.Controllers";
+ }
+
+ private static string GetUniqueFileName(string controllerName, IDictionary controllerNamesInUse)
+ {
+ // We emit unique file names to prevent a failure in the source generator, but also because our test suite
+ // may contain two resources with the same class name in different namespaces. That works, as long as only
+ // one of its controllers gets registered.
+
+ if (controllerNamesInUse.TryGetValue(controllerName, out int lastIndex))
+ {
+ lastIndex++;
+ controllerNamesInUse[controllerName] = lastIndex;
+
+ return $"{controllerName}{lastIndex}.g.cs";
+ }
+
+ controllerNamesInUse[controllerName] = 1;
+ return $"{controllerName}.g.cs";
+ }
+ }
+}
diff --git a/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj b/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj
new file mode 100644
index 00000000..c94885e7
--- /dev/null
+++ b/src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj
@@ -0,0 +1,53 @@
+
+
+ $(JsonApiDotNetCoreVersionPrefix)
+ netstandard2.0
+ true
+ true
+ false
+ $(NoWarn);NU5128
+ disable
+ true
+
+
+
+ jsonapidotnetcore;jsonapi;json:api;dotnet;asp.net;rest;web-api
+ Source generators for JsonApiDotNetCore
+ json-api-dotnet
+ https://www.jsonapi.net/
+ MIT
+ false
+ See https://github.com/json-api-dotnet/JsonApiDotNetCore/releases.
+ logo.png
+ https://github.com/json-api-dotnet/JsonApiDotNetCore
+
+
+
+
+ True
+
+
+
+
+
+
+
+
+
+
+
+ $(GetTargetPathDependsOn);GetDependencyTargetPaths
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JsonApiDotNetCore.SourceGenerators/JsonApiEndpointsCopy.cs b/src/JsonApiDotNetCore.SourceGenerators/JsonApiEndpointsCopy.cs
new file mode 100644
index 00000000..14134adc
--- /dev/null
+++ b/src/JsonApiDotNetCore.SourceGenerators/JsonApiEndpointsCopy.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace JsonApiDotNetCore.SourceGenerators
+{
+ // IMPORTANT: A copy of this type exists in the JsonApiDotNetCore project. Keep these in sync when making changes.
+ [Flags]
+ public enum JsonApiEndpointsCopy
+ {
+ None = 0,
+ GetCollection = 1,
+ GetSingle = 1 << 1,
+ GetSecondary = 1 << 2,
+ GetRelationship = 1 << 3,
+ Post = 1 << 4,
+ PostRelationship = 1 << 5,
+ Patch = 1 << 6,
+ PatchRelationship = 1 << 7,
+ Delete = 1 << 8,
+ DeleteRelationship = 1 << 9,
+
+ Query = GetCollection | GetSingle | GetSecondary | GetRelationship,
+ Command = Post | PostRelationship | Patch | PatchRelationship | Delete | DeleteRelationship,
+
+ All = Query | Command
+ }
+}
diff --git a/src/JsonApiDotNetCore.SourceGenerators/Properties/launchSettings.json b/src/JsonApiDotNetCore.SourceGenerators/Properties/launchSettings.json
new file mode 100644
index 00000000..2679b059
--- /dev/null
+++ b/src/JsonApiDotNetCore.SourceGenerators/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "JsonApiDotNetCore.SourceGenerators": {
+ "commandName": "DebugRoslynComponent",
+ "targetProject": "..\\..\\test\\SourceGeneratorDebugger\\SourceGeneratorDebugger.csproj"
+ }
+ }
+}
diff --git a/src/JsonApiDotNetCore.SourceGenerators/SourceCodeWriter.cs b/src/JsonApiDotNetCore.SourceGenerators/SourceCodeWriter.cs
new file mode 100644
index 00000000..a5425931
--- /dev/null
+++ b/src/JsonApiDotNetCore.SourceGenerators/SourceCodeWriter.cs
@@ -0,0 +1,275 @@
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace JsonApiDotNetCore.SourceGenerators
+{
+ ///
+ /// Writes the source code for an ASP.NET controller for a JSON:API resource.
+ ///
+ internal sealed class SourceCodeWriter
+ {
+ private const int SpacesPerIndent = 4;
+
+ private static readonly IDictionary IndentTable = new Dictionary
+ {
+ [0] = string.Empty,
+ [1] = new string(' ', 1 * SpacesPerIndent),
+ [2] = new string(' ', 2 * SpacesPerIndent),
+ [3] = new string(' ', 3 * SpacesPerIndent),
+ [4] = new string(' ', 4 * SpacesPerIndent)
+ };
+
+ private static readonly IDictionary AggregateEndpointToServiceNameMap =
+ new Dictionary
+ {
+ [JsonApiEndpointsCopy.All] = ("IResourceService", "resourceService"),
+ [JsonApiEndpointsCopy.Query] = ("IResourceQueryService", "queryService"),
+ [JsonApiEndpointsCopy.Command] = ("IResourceCommandService", "commandService")
+ };
+
+ private static readonly IDictionary EndpointToServiceNameMap =
+ new Dictionary
+ {
+ [JsonApiEndpointsCopy.GetCollection] = ("IGetAllService", "getAll"),
+ [JsonApiEndpointsCopy.GetSingle] = ("IGetByIdService", "getById"),
+ [JsonApiEndpointsCopy.GetSecondary] = ("IGetSecondaryService", "getSecondary"),
+ [JsonApiEndpointsCopy.GetRelationship] = ("IGetRelationshipService", "getRelationship"),
+ [JsonApiEndpointsCopy.Post] = ("ICreateService", "create"),
+ [JsonApiEndpointsCopy.PostRelationship] = ("IAddToRelationshipService", "addToRelationship"),
+ [JsonApiEndpointsCopy.Patch] = ("IUpdateService", "update"),
+ [JsonApiEndpointsCopy.PatchRelationship] = ("ISetRelationshipService", "setRelationship"),
+ [JsonApiEndpointsCopy.Delete] = ("IDeleteService", "delete"),
+ [JsonApiEndpointsCopy.DeleteRelationship] = ("IRemoveFromRelationshipService", "removeFromRelationship")
+ };
+
+ private readonly GeneratorExecutionContext _context;
+ private readonly DiagnosticDescriptor _missingIndentInTableErrorDescriptor;
+
+ private readonly StringBuilder _sourceBuilder = new StringBuilder();
+ private int _depth;
+
+ public SourceCodeWriter(GeneratorExecutionContext context, DiagnosticDescriptor missingIndentInTableErrorDescriptor)
+ {
+ _context = context;
+ _missingIndentInTableErrorDescriptor = missingIndentInTableErrorDescriptor;
+ }
+
+ public string Write(INamedTypeSymbol resourceType, ITypeSymbol idType, JsonApiEndpointsCopy endpointsToGenerate, string controllerNamespace,
+ string controllerName, INamedTypeSymbol loggerFactoryInterface)
+ {
+ _sourceBuilder.Clear();
+ _depth = 0;
+
+ if (idType.IsReferenceType && idType.NullableAnnotation == NullableAnnotation.Annotated)
+ {
+ WriteNullableEnable();
+ }
+
+ WriteNamespaceImports(loggerFactoryInterface, resourceType);
+
+ if (controllerNamespace != null)
+ {
+ WriteOpenNamespaceDeclaration(controllerNamespace);
+ _depth++;
+ }
+
+ WriteOpenClassDeclaration(controllerName, endpointsToGenerate, resourceType, idType);
+ _depth++;
+
+ WriteConstructor(controllerName, loggerFactoryInterface, endpointsToGenerate, resourceType, idType);
+
+ _depth--;
+ WriteCloseCurly();
+
+ if (controllerNamespace != null)
+ {
+ _depth--;
+ WriteCloseCurly();
+ }
+
+ return _sourceBuilder.ToString();
+ }
+
+ private void WriteNullableEnable()
+ {
+ _sourceBuilder.AppendLine("#nullable enable");
+ _sourceBuilder.AppendLine();
+ }
+
+ private void WriteNamespaceImports(INamedTypeSymbol loggerFactoryInterface, INamedTypeSymbol resourceType)
+ {
+ _sourceBuilder.AppendLine($@"using {loggerFactoryInterface.ContainingNamespace};");
+
+ _sourceBuilder.AppendLine("using JsonApiDotNetCore.Configuration;");
+ _sourceBuilder.AppendLine("using JsonApiDotNetCore.Controllers;");
+ _sourceBuilder.AppendLine("using JsonApiDotNetCore.Services;");
+
+ if (!resourceType.ContainingNamespace.IsGlobalNamespace)
+ {
+ _sourceBuilder.AppendLine($"using {resourceType.ContainingNamespace};");
+ }
+
+ _sourceBuilder.AppendLine();
+ }
+
+ private void WriteOpenNamespaceDeclaration(string controllerNamespace)
+ {
+ _sourceBuilder.AppendLine($"namespace {controllerNamespace}");
+
+ WriteOpenCurly();
+ }
+
+ private void WriteOpenClassDeclaration(string controllerName, JsonApiEndpointsCopy endpointsToGenerate, INamedTypeSymbol resourceType,
+ ITypeSymbol idType)
+ {
+ string baseClassName = GetControllerBaseClassName(endpointsToGenerate);
+
+ WriteIndent();
+ _sourceBuilder.AppendLine($@"public sealed partial class {controllerName} : {baseClassName}<{resourceType.Name}, {idType}>");
+
+ WriteOpenCurly();
+ }
+
+ private static string GetControllerBaseClassName(JsonApiEndpointsCopy endpointsToGenerate)
+ {
+ switch (endpointsToGenerate)
+ {
+ case JsonApiEndpointsCopy.Query:
+ {
+ return "JsonApiQueryController";
+ }
+ case JsonApiEndpointsCopy.Command:
+ {
+ return "JsonApiCommandController";
+ }
+ default:
+ {
+ return "JsonApiController";
+ }
+ }
+ }
+
+ private void WriteConstructor(string controllerName, INamedTypeSymbol loggerFactoryInterface, JsonApiEndpointsCopy endpointsToGenerate,
+ INamedTypeSymbol resourceType, ITypeSymbol idType)
+ {
+ string loggerName = loggerFactoryInterface.Name;
+
+ WriteIndent();
+ _sourceBuilder.AppendLine($"public {controllerName}(IJsonApiOptions options, IResourceGraph resourceGraph, {loggerName} loggerFactory,");
+
+ _depth++;
+
+ if (AggregateEndpointToServiceNameMap.TryGetValue(endpointsToGenerate, out (string ServiceName, string ParameterName) value))
+ {
+ WriteParameterListForShortConstructor(value.ServiceName, value.ParameterName, resourceType, idType);
+ }
+ else
+ {
+ WriteParameterListForLongConstructor(endpointsToGenerate, resourceType, idType);
+ }
+
+ _depth--;
+
+ WriteOpenCurly();
+ WriteCloseCurly();
+ }
+
+ private void WriteParameterListForShortConstructor(string serviceName, string parameterName, INamedTypeSymbol resourceType, ITypeSymbol idType)
+ {
+ WriteIndent();
+ _sourceBuilder.AppendLine($"{serviceName}<{resourceType.Name}, {idType}> {parameterName})");
+
+ WriteIndent();
+ _sourceBuilder.AppendLine($": base(options, resourceGraph, loggerFactory, {parameterName})");
+ }
+
+ private void WriteParameterListForLongConstructor(JsonApiEndpointsCopy endpointsToGenerate, INamedTypeSymbol resourceType, ITypeSymbol idType)
+ {
+ bool isFirstEntry = true;
+
+ foreach (KeyValuePair entry in EndpointToServiceNameMap)
+ {
+ if ((endpointsToGenerate & entry.Key) == entry.Key)
+ {
+ if (isFirstEntry)
+ {
+ isFirstEntry = false;
+ }
+ else
+ {
+ _sourceBuilder.AppendLine(Tokens.Comma);
+ }
+
+ WriteIndent();
+ _sourceBuilder.Append($"{entry.Value.ServiceName}<{resourceType.Name}, {idType}> {entry.Value.ParameterName}");
+ }
+ }
+
+ _sourceBuilder.AppendLine(Tokens.CloseParen);
+
+ WriteIndent();
+ _sourceBuilder.AppendLine(": base(options, resourceGraph, loggerFactory,");
+
+ isFirstEntry = true;
+ _depth++;
+
+ foreach (KeyValuePair entry in EndpointToServiceNameMap)
+ {
+ if ((endpointsToGenerate & entry.Key) == entry.Key)
+ {
+ if (isFirstEntry)
+ {
+ isFirstEntry = false;
+ }
+ else
+ {
+ _sourceBuilder.AppendLine(Tokens.Comma);
+ }
+
+ WriteIndent();
+ _sourceBuilder.Append($"{entry.Value.ParameterName}: {entry.Value.ParameterName}");
+ }
+ }
+
+ _sourceBuilder.AppendLine(Tokens.CloseParen);
+ _depth--;
+ }
+
+ private void WriteOpenCurly()
+ {
+ WriteIndent();
+ _sourceBuilder.AppendLine(Tokens.OpenCurly);
+ }
+
+ private void WriteCloseCurly()
+ {
+ WriteIndent();
+ _sourceBuilder.AppendLine(Tokens.CloseCurly);
+ }
+
+ private void WriteIndent()
+ {
+ // PERF: Reuse pre-calculated indents instead of allocating a new string each time.
+ if (!IndentTable.TryGetValue(_depth, out string indent))
+ {
+ var diagnostic = Diagnostic.Create(_missingIndentInTableErrorDescriptor, Location.None, _depth.ToString());
+ _context.ReportDiagnostic(diagnostic);
+
+ indent = new string(' ', _depth * SpacesPerIndent);
+ }
+
+ _sourceBuilder.Append(indent);
+ }
+
+#pragma warning disable AV1008 // Class should not be static
+ private static class Tokens
+ {
+ public const string OpenCurly = "{";
+ public const string CloseCurly = "}";
+ public const string CloseParen = ")";
+ public const string Comma = ",";
+ }
+#pragma warning restore AV1008 // Class should not be static
+ }
+}
diff --git a/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs b/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs
new file mode 100644
index 00000000..0fbc18a7
--- /dev/null
+++ b/src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace JsonApiDotNetCore.SourceGenerators
+{
+ ///
+ /// Collects type declarations in the project that have at least one attribute on them. Because this receiver operates at the syntax level, we cannot
+ /// check for the expected attribute. This must be done during semantic analysis, because source code may contain any of these:
+ /// { }
+ ///
+ /// [ResourceAttribute]
+ /// public class ExampleResource2 : Identifiable { }
+ ///
+ /// [AlternateNamespaceName.Annotations.Resource]
+ /// public class ExampleResource3 : Identifiable { }
+ ///
+ /// [AlternateTypeName]
+ /// public class ExampleResource4 : Identifiable { }
+ /// ]]>
+ ///
+ ///
+ internal sealed class TypeWithAttributeSyntaxReceiver : ISyntaxReceiver
+ {
+ public readonly ISet TypeDeclarations = new HashSet();
+
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ {
+ if (syntaxNode is TypeDeclarationSyntax typeDeclarationSyntax && typeDeclarationSyntax.AttributeLists.Any())
+ {
+ TypeDeclarations.Add(typeDeclarationSyntax);
+ }
+ }
+ }
+}
diff --git a/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs
index 53e206cc..52287953 100644
--- a/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs
+++ b/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs
@@ -159,7 +159,7 @@ private ResourceType CreateResourceType(string publicName, Type resourceClrType,
AssertNoDuplicatePublicName(attributes, relationships);
- var linksAttribute = (ResourceLinksAttribute?)resourceClrType.GetCustomAttribute(typeof(ResourceLinksAttribute));
+ var linksAttribute = resourceClrType.GetCustomAttribute(true);
return linksAttribute == null
? new ResourceType(publicName, resourceClrType, idClrType, attributes, relationships, eagerLoads)
@@ -189,7 +189,7 @@ private IReadOnlyCollection GetAttributes(Type resourceClrType)
continue;
}
- var attribute = (AttrAttribute?)property.GetCustomAttribute(typeof(AttrAttribute));
+ var attribute = property.GetCustomAttribute(true);
if (attribute == null)
{
@@ -222,7 +222,7 @@ private IReadOnlyCollection GetRelationships(Type resourc
foreach (PropertyInfo property in properties)
{
- var relationship = (RelationshipAttribute?)property.GetCustomAttribute(typeof(RelationshipAttribute));
+ var relationship = property.GetCustomAttribute(true);
if (relationship != null)
{
@@ -261,7 +261,7 @@ private IReadOnlyCollection GetEagerLoads(Type resourceClrTy
foreach (PropertyInfo property in properties)
{
- var eagerLoad = (EagerLoadAttribute?)property.GetCustomAttribute(typeof(EagerLoadAttribute));
+ var eagerLoad = property.GetCustomAttribute(true);
if (eagerLoad == null)
{
diff --git a/src/JsonApiDotNetCore/Configuration/ResourceNameFormatter.cs b/src/JsonApiDotNetCore/Configuration/ResourceNameFormatter.cs
index c6be029b..4ce4b09a 100644
--- a/src/JsonApiDotNetCore/Configuration/ResourceNameFormatter.cs
+++ b/src/JsonApiDotNetCore/Configuration/ResourceNameFormatter.cs
@@ -22,9 +22,11 @@ public string FormatResourceName(Type resourceClrType)
{
ArgumentGuard.NotNull(resourceClrType, nameof(resourceClrType));
- if (resourceClrType.GetCustomAttribute(typeof(ResourceAttribute)) is ResourceAttribute attribute)
+ var resourceAttribute = resourceClrType.GetCustomAttribute(true);
+
+ if (resourceAttribute != null && !string.IsNullOrWhiteSpace(resourceAttribute.PublicName))
{
- return attribute.PublicName;
+ return resourceAttribute.PublicName;
}
string publicName = resourceClrType.Name.Pluralize();
diff --git a/src/JsonApiDotNetCore/Controllers/Annotations/DisableQueryStringAttribute.cs b/src/JsonApiDotNetCore/Controllers/Annotations/DisableQueryStringAttribute.cs
index bfa842ed..f1d10776 100644
--- a/src/JsonApiDotNetCore/Controllers/Annotations/DisableQueryStringAttribute.cs
+++ b/src/JsonApiDotNetCore/Controllers/Annotations/DisableQueryStringAttribute.cs
@@ -18,7 +18,7 @@ namespace JsonApiDotNetCore.Controllers.Annotations
/// public class CustomersController : JsonApiController { }
/// ]]>
[PublicAPI]
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class DisableQueryStringAttribute : Attribute
{
public static readonly DisableQueryStringAttribute Empty = new(JsonApiQueryStringParameters.None);
diff --git a/src/JsonApiDotNetCore/Controllers/Annotations/DisableRoutingConventionAttribute.cs b/src/JsonApiDotNetCore/Controllers/Annotations/DisableRoutingConventionAttribute.cs
index 34dc0c97..93db0d7d 100644
--- a/src/JsonApiDotNetCore/Controllers/Annotations/DisableRoutingConventionAttribute.cs
+++ b/src/JsonApiDotNetCore/Controllers/Annotations/DisableRoutingConventionAttribute.cs
@@ -11,7 +11,7 @@ namespace JsonApiDotNetCore.Controllers.Annotations
/// public class CustomersController : JsonApiController { }
/// ]]>
[PublicAPI]
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class DisableRoutingConventionAttribute : Attribute
{
}
diff --git a/src/JsonApiDotNetCore/Controllers/JsonApiEndpoints.cs b/src/JsonApiDotNetCore/Controllers/JsonApiEndpoints.cs
new file mode 100644
index 00000000..5fa0c129
--- /dev/null
+++ b/src/JsonApiDotNetCore/Controllers/JsonApiEndpoints.cs
@@ -0,0 +1,31 @@
+using System;
+using JetBrains.Annotations;
+
+// ReSharper disable CheckNamespace
+#pragma warning disable AV1505 // Namespace should match with assembly name
+
+namespace JsonApiDotNetCore.Controllers
+{
+ // IMPORTANT: An internal copy of this type exists in the SourceGenerators project. Keep these in sync when making changes.
+ [PublicAPI]
+ [Flags]
+ public enum JsonApiEndpoints
+ {
+ None = 0,
+ GetCollection = 1,
+ GetSingle = 1 << 1,
+ GetSecondary = 1 << 2,
+ GetRelationship = 1 << 3,
+ Post = 1 << 4,
+ PostRelationship = 1 << 5,
+ Patch = 1 << 6,
+ PatchRelationship = 1 << 7,
+ Delete = 1 << 8,
+ DeleteRelationship = 1 << 9,
+
+ Query = GetCollection | GetSingle | GetSecondary | GetRelationship,
+ Command = Post | PostRelationship | Patch | PatchRelationship | Delete | DeleteRelationship,
+
+ All = Query | Command
+ }
+}
diff --git a/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs b/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs
index 6777412f..b9924d92 100644
--- a/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs
+++ b/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs
@@ -374,7 +374,7 @@ public static string GetPublicNameForProperty(PropertyInfo property)
{
ArgumentGuard.NotNull(property, nameof(property));
- var jsonNameAttribute = (JsonPropertyNameAttribute?)property.GetCustomAttribute(typeof(JsonPropertyNameAttribute));
+ var jsonNameAttribute = property.GetCustomAttribute(true);
return jsonNameAttribute?.Name ?? property.Name;
}
}
diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
index 682a136a..f69d39d6 100644
--- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
+++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
@@ -1,12 +1,13 @@
- 5.0.0
+ $(JsonApiDotNetCoreVersionPrefix)
$(NetCoreAppVersion)
+ true
true
- jsonapidotnetcore;jsonapi;json:api;dotnet;asp.net
+ jsonapidotnetcore;jsonapi;json:api;dotnet;asp.net;rest;web-api
A framework for building JSON:API compliant REST APIs using ASP.NET and Entity Framework Core. Includes support for Atomic Operations. The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features such as sorting, filtering and pagination. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection making extensibility incredibly easy.
json-api-dotnet
https://www.jsonapi.net/
@@ -20,19 +21,23 @@
-
+
True
+
+
+
+
-
+
diff --git a/src/JsonApiDotNetCore/Middleware/AsyncQueryStringActionFilter.cs b/src/JsonApiDotNetCore/Middleware/AsyncQueryStringActionFilter.cs
index 7a72874e..85ce5046 100644
--- a/src/JsonApiDotNetCore/Middleware/AsyncQueryStringActionFilter.cs
+++ b/src/JsonApiDotNetCore/Middleware/AsyncQueryStringActionFilter.cs
@@ -26,7 +26,7 @@ public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionE
if (context.HttpContext.IsJsonApiRequest())
{
- var disableQueryStringAttribute = context.Controller.GetType().GetCustomAttribute();
+ var disableQueryStringAttribute = context.Controller.GetType().GetCustomAttribute(true);
_queryStringReader.ReadAll(disableQueryStringAttribute);
}
diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs b/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs
index d304fc9d..bd56c411 100644
--- a/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs
+++ b/src/JsonApiDotNetCore/Middleware/JsonApiRoutingConvention.cs
@@ -81,6 +81,11 @@ public void Apply(ApplicationModel application)
if (resourceType != null)
{
+ if (_controllerPerResourceTypeMap.ContainsKey(resourceType))
+ {
+ throw new InvalidConfigurationException($"Multiple controllers found for resource type '{resourceType}'.");
+ }
+
_resourceTypePerControllerTypeMap.Add(controller.ControllerType, resourceType);
_controllerPerResourceTypeMap.Add(resourceType, controller);
}
@@ -117,7 +122,7 @@ public void Apply(ApplicationModel application)
private bool IsRoutingConventionEnabled(ControllerModel controller)
{
return controller.ControllerType.IsSubclassOf(typeof(CoreJsonApiController)) &&
- controller.ControllerType.GetCustomAttribute() == null;
+ controller.ControllerType.GetCustomAttribute(true) == null;
}
///
diff --git a/src/JsonApiDotNetCore/Resources/Annotations/ResourceAttribute.cs b/src/JsonApiDotNetCore/Resources/Annotations/ResourceAttribute.cs
index 501d085d..4bb83a4e 100644
--- a/src/JsonApiDotNetCore/Resources/Annotations/ResourceAttribute.cs
+++ b/src/JsonApiDotNetCore/Resources/Annotations/ResourceAttribute.cs
@@ -1,26 +1,34 @@
using System;
using JetBrains.Annotations;
+using JsonApiDotNetCore.Controllers;
+
+// ReSharper disable CheckNamespace
+#pragma warning disable AV1505 // Namespace should match with assembly name
namespace JsonApiDotNetCore.Resources.Annotations
{
///
- /// When put on a resource class, overrides the convention-based resource name.
+ /// When put on a resource class, overrides the convention-based public resource name and auto-generates an ASP.NET controller.
///
[PublicAPI]
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class ResourceAttribute : Attribute
{
///
- /// The publicly exposed name of this resource type. When not explicitly assigned, the configured naming convention is applied on the pluralized resource
- /// class name.
+ /// Optional. The publicly exposed name of this resource type.
///
- public string PublicName { get; }
+ public string? PublicName { get; set; }
- public ResourceAttribute(string publicName)
- {
- ArgumentGuard.NotNullNorEmpty(publicName, nameof(publicName));
+ ///
+ /// The set of endpoints to auto-generate an ASP.NET controller for. Defaults to . Set to
+ /// to disable controller generation.
+ ///
+ public JsonApiEndpoints GenerateControllerEndpoints { get; set; } = JsonApiEndpoints.All;
- PublicName = publicName;
- }
+ ///
+ /// Optional. The full namespace in which to auto-generate the ASP.NET controller. Defaults to the sibling namespace "Controllers". For example, a
+ /// resource class that is declared in namespace "ExampleCompany.ExampleApi.Models" will use "ExampleCompany.ExampleApi.Controllers" by default.
+ ///
+ public string? ControllerNamespace { get; set; }
}
}
diff --git a/src/JsonApiDotNetCore/Resources/Annotations/ResourceLinksAttribute.cs b/src/JsonApiDotNetCore/Resources/Annotations/ResourceLinksAttribute.cs
index f4d3a1ff..3e513d71 100644
--- a/src/JsonApiDotNetCore/Resources/Annotations/ResourceLinksAttribute.cs
+++ b/src/JsonApiDotNetCore/Resources/Annotations/ResourceLinksAttribute.cs
@@ -8,7 +8,7 @@ namespace JsonApiDotNetCore.Resources.Annotations
/// When put on a resource class, overrides global configuration for which links to render.
///
[PublicAPI]
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class ResourceLinksAttribute : Attribute
{
///
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/BroadcastComment.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/BroadcastComment.cs
index 1a5e733d..7c101a8d 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/BroadcastComment.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/BroadcastComment.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Archiving")]
public sealed class BroadcastComment : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/BroadcastCommentsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/BroadcastCommentsController.cs
deleted file mode 100644
index d801d2ea..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/BroadcastCommentsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
-{
- public sealed class BroadcastCommentsController : JsonApiController
- {
- public BroadcastCommentsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionBroadcast.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionBroadcast.cs
index 07c57183..a57efcfd 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionBroadcast.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionBroadcast.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Archiving")]
public sealed class TelevisionBroadcast : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionBroadcastsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionBroadcastsController.cs
deleted file mode 100644
index d5cd933b..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionBroadcastsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
-{
- public sealed class TelevisionBroadcastsController : JsonApiController
- {
- public TelevisionBroadcastsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionNetwork.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionNetwork.cs
index fb4dafda..70c12b6b 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionNetwork.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionNetwork.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Archiving")]
public sealed class TelevisionNetwork : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionNetworksController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionNetworksController.cs
deleted file mode 100644
index 4b981a85..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionNetworksController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
-{
- public sealed class TelevisionNetworksController : JsonApiController
- {
- public TelevisionNetworksController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionStation.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionStation.cs
index 9f49047d..d929fd38 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionStation.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionStation.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Archiving")]
public sealed class TelevisionStation : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionStationsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionStationsController.cs
deleted file mode 100644
index 4ab018ea..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionStationsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Archiving
-{
- public sealed class TelevisionStationsController : JsonApiController
- {
- public TelevisionStationsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Lyric.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Lyric.cs
index e39e52ff..ba3ced81 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Lyric.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Lyric.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations")]
public sealed class Lyric : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/LyricsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/LyricsController.cs
deleted file mode 100644
index 24936bab..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/LyricsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
-{
- public sealed class LyricsController : JsonApiController
- {
- public LyricsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTrack.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTrack.cs
index 8e3071ae..e2e7710e 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTrack.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTrack.cs
@@ -8,6 +8,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations")]
public sealed class MusicTrack : Identifiable
{
[RegularExpression(@"(?im)^[{(]?[0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$")]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTracksController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTracksController.cs
deleted file mode 100644
index 697ba4a0..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTracksController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
-{
- public sealed class MusicTracksController : JsonApiController
- {
- public MusicTracksController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Performer.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Performer.cs
index d0403e34..31f9d2f8 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Performer.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Performer.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations")]
public sealed class Performer : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/PerformersController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/PerformersController.cs
deleted file mode 100644
index 59c5dfc6..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/PerformersController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
-{
- public sealed class PerformersController : JsonApiController
- {
- public PerformersController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Playlist.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Playlist.cs
index 43d05609..cc4d9cb4 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Playlist.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/Playlist.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations")]
public sealed class Playlist : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/PlaylistsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/PlaylistsController.cs
deleted file mode 100644
index 1b893615..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/PlaylistsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
-{
- public sealed class PlaylistsController : JsonApiController
- {
- public PlaylistsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/RecordCompaniesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/RecordCompaniesController.cs
deleted file mode 100644
index 6cc63b23..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/RecordCompaniesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
-{
- public sealed class RecordCompaniesController : JsonApiController
- {
- public RecordCompaniesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/RecordCompany.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/RecordCompany.cs
index b8ab7be5..bf1abab6 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/RecordCompany.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/RecordCompany.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations")]
public sealed class RecordCompany : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/TextLanguage.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/TextLanguage.cs
index 46068583..10733b4b 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/TextLanguage.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/TextLanguage.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations")]
public sealed class TextLanguage : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/TextLanguagesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/TextLanguagesController.cs
deleted file mode 100644
index b2d26833..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/TextLanguagesController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.AtomicOperations
-{
- public sealed class TextLanguagesController : JsonApiController
- {
- public TextLanguagesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Car.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Car.cs
index 9146bf86..5e7b5cc9 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Car.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Car.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys")]
public sealed class Car : Identifiable
{
[NotMapped]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarsController.cs
deleted file mode 100644
index aa0a2099..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys
-{
- public sealed class CarsController : JsonApiController
- {
- public CarsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Dealership.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Dealership.cs
index 42d11da7..651eeb69 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Dealership.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Dealership.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys")]
public sealed class Dealership : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/DealershipsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/DealershipsController.cs
deleted file mode 100644
index 2ec7d85c..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/DealershipsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys
-{
- public sealed class DealershipsController : JsonApiController
- {
- public DealershipsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Engine.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Engine.cs
index 2a322e75..87e207c0 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Engine.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/Engine.cs
@@ -5,6 +5,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys")]
public sealed class Engine : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/EnginesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/EnginesController.cs
deleted file mode 100644
index f995a722..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/EnginesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.CompositeKeys
-{
- public sealed class EnginesController : JsonApiController
- {
- public EnginesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/PoliciesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/PoliciesController.cs
deleted file mode 100644
index 4edb2dfd..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/PoliciesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.ContentNegotiation
-{
- public sealed class PoliciesController : JsonApiController
- {
- public PoliciesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/Policy.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/Policy.cs
index 27d85010..9f8e8b2a 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/Policy.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/Policy.cs
@@ -5,6 +5,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.ContentNegotiation
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ContentNegotiation")]
public sealed class Policy : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/Toothbrush.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/Toothbrush.cs
index 674513f9..e79ba4c1 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/Toothbrush.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/Toothbrush.cs
@@ -5,6 +5,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.ControllerActionResults
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ControllerActionResults")]
public sealed class Toothbrush : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs
index 92bdf571..fbebfa42 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ControllerActionResults/ToothbrushesController.cs
@@ -1,16 +1,17 @@
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Serialization.Objects;
-using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
namespace JsonApiDotNetCoreTests.IntegrationTests.ControllerActionResults
{
- public sealed class ToothbrushesController : BaseJsonApiController
+ // Workaround for https://youtrack.jetbrains.com/issue/RSRP-487028
+ public partial class ToothbrushesController
+ {
+ }
+
+ partial class ToothbrushesController
{
internal const int EmptyActionResultId = 11111111;
internal const int ActionResultWithErrorObjectId = 22222222;
@@ -18,12 +19,6 @@ public sealed class ToothbrushesController : BaseJsonApiController resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
-
[HttpGet("{id}")]
public override async Task GetAsync(int id, CancellationToken cancellationToken)
{
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/Civilian.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/Civilian.cs
index 13141fec..f0c67c80 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/Civilian.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/Civilian.cs
@@ -5,6 +5,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.CustomRoutes
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.CustomRoutes")]
public sealed class Civilian : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CiviliansController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CiviliansController.cs
index aad9ccb4..b94f7b89 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CiviliansController.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/CiviliansController.cs
@@ -1,24 +1,19 @@
using System.Threading.Tasks;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Controllers.Annotations;
-using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
namespace JsonApiDotNetCoreTests.IntegrationTests.CustomRoutes
{
+ // Workaround for https://youtrack.jetbrains.com/issue/RSRP-487028
+ public partial class CiviliansController
+ {
+ }
+
[ApiController]
[DisableRoutingConvention]
[Route("world-civilians")]
- public sealed class CiviliansController : JsonApiController
+ partial class CiviliansController
{
- public CiviliansController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
-
[HttpGet("missing")]
public async Task GetMissingAsync()
{
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/Town.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/Town.cs
index ca893947..fd98d4c0 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/Town.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/Town.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.CustomRoutes
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.CustomRoutes")]
public sealed class Town : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/TownsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/TownsController.cs
index 79b9cbc6..3369ac76 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/TownsController.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CustomRoutes/TownsController.cs
@@ -3,21 +3,27 @@
using System.Threading;
using System.Threading.Tasks;
using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Controllers.Annotations;
using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace JsonApiDotNetCoreTests.IntegrationTests.CustomRoutes
{
+ // Workaround for https://youtrack.jetbrains.com/issue/RSRP-487028
+ public partial class TownsController
+ {
+ }
+
[DisableRoutingConvention]
[Route("world-api/civilization/popular/towns")]
- public sealed class TownsController : JsonApiController
+ partial class TownsController
{
private readonly CustomRouteDbContext _dbContext;
+ [ActivatorUtilitiesConstructor]
public TownsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IResourceService resourceService,
CustomRouteDbContext dbContext)
: base(options, resourceGraph, loggerFactory, resourceService)
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs
index eb4a3d2f..519d6288 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.EagerLoading
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.EagerLoading")]
public sealed class Building : Identifiable
{
private string? _tempPrimaryDoorColor;
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/BuildingsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/BuildingsController.cs
deleted file mode 100644
index f26107ba..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/BuildingsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.EagerLoading
-{
- public sealed class BuildingsController : JsonApiController
- {
- public BuildingsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/State.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/State.cs
index e15f5e2e..afd641b8 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/State.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/State.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.EagerLoading
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.EagerLoading")]
public sealed class State : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/StatesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/StatesController.cs
deleted file mode 100644
index 5e792ade..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/StatesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.EagerLoading
-{
- public sealed class StatesController : JsonApiController
- {
- public StatesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Street.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Street.cs
index d6aa1a97..736b0535 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Street.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Street.cs
@@ -8,6 +8,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.EagerLoading
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.EagerLoading")]
public sealed class Street : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/StreetsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/StreetsController.cs
deleted file mode 100644
index 19aab24d..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/StreetsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.EagerLoading
-{
- public sealed class StreetsController : JsonApiController
- {
- public StreetsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ConsumerArticle.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ConsumerArticle.cs
index 4c914b05..f000a792 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ConsumerArticle.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ConsumerArticle.cs
@@ -5,6 +5,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.ExceptionHandling
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ExceptionHandling")]
public sealed class ConsumerArticle : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ConsumerArticlesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ConsumerArticlesController.cs
deleted file mode 100644
index c7e13af0..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ConsumerArticlesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.ExceptionHandling
-{
- public sealed class ConsumerArticlesController : JsonApiController
- {
- public ConsumerArticlesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ThrowingArticle.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ThrowingArticle.cs
index d0a3ce81..210a2bb5 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ThrowingArticle.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ThrowingArticle.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.ExceptionHandling
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ExceptionHandling")]
public sealed class ThrowingArticle : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ThrowingArticlesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ThrowingArticlesController.cs
deleted file mode 100644
index d518902f..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ThrowingArticlesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.ExceptionHandling
-{
- public sealed class ThrowingArticlesController : JsonApiController
- {
- public ThrowingArticlesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/ArtGalleriesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/ArtGalleriesController.cs
deleted file mode 100644
index adec4751..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/ArtGalleriesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.HostingInIIS
-{
- public sealed class ArtGalleriesController : JsonApiController
- {
- public ArtGalleriesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/ArtGallery.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/ArtGallery.cs
index 34bec33b..d6131d07 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/ArtGallery.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/ArtGallery.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.HostingInIIS
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.HostingInIIS")]
public sealed class ArtGallery : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/Painting.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/Painting.cs
index f2dd3082..52508dbd 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/Painting.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/Painting.cs
@@ -5,6 +5,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.HostingInIIS
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.HostingInIIS")]
public sealed class Painting : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/PaintingsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/PaintingsController.cs
index 6dcc0d93..3217f345 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/PaintingsController.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/PaintingsController.cs
@@ -1,20 +1,16 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Controllers.Annotations;
-using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
namespace JsonApiDotNetCoreTests.IntegrationTests.HostingInIIS
{
+ // Workaround for https://youtrack.jetbrains.com/issue/RSRP-487028
+ public partial class PaintingsController
+ {
+ }
+
[DisableRoutingConvention]
[Route("custom/path/to/paintings-of-the-world")]
- public sealed class PaintingsController : JsonApiController
+ partial class PaintingsController
{
- public PaintingsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
}
}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemDirectoriesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemDirectoriesController.cs
deleted file mode 100644
index 0c4af4fa..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemDirectoriesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState
-{
- public sealed class SystemDirectoriesController : JsonApiController
- {
- public SystemDirectoriesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemDirectory.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemDirectory.cs
index 549356db..caafbb66 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemDirectory.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemDirectory.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState")]
public sealed class SystemDirectory : Identifiable
{
[RegularExpression("^[0-9]+$")]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemFile.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemFile.cs
index 7a8d796a..c5143f34 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemFile.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemFile.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState")]
public sealed class SystemFile : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemFilesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemFilesController.cs
deleted file mode 100644
index 90fb26d2..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemFilesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState
-{
- public sealed class SystemFilesController : JsonApiController
- {
- public SystemFilesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemVolume.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemVolume.cs
index b2c4ede2..2f1453cb 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemVolume.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemVolume.cs
@@ -5,6 +5,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState")]
public sealed class SystemVolume : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemVolumesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemVolumesController.cs
deleted file mode 100644
index a649619e..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemVolumesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.ModelState
-{
- public sealed class SystemVolumesController : JsonApiController
- {
- public SystemVolumesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/Workflow.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/Workflow.cs
index 127d6635..042fca18 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/Workflow.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/Workflow.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.RequestBody
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.InputValidation.RequestBody")]
public sealed class Workflow : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/WorkflowsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/WorkflowsController.cs
deleted file mode 100644
index b31523b7..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/WorkflowsController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.InputValidation.RequestBody
-{
- public sealed class WorkflowsController : JsonApiController
- {
- public WorkflowsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/Photo.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/Photo.cs
index 1ee92f0d..a3b3e3b3 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/Photo.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/Photo.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Links
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Links")]
public sealed class Photo : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoAlbum.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoAlbum.cs
index 9f6df3c0..5e9ac091 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoAlbum.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoAlbum.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Links
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Links")]
public sealed class PhotoAlbum : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoAlbumsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoAlbumsController.cs
deleted file mode 100644
index 29f80bc7..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoAlbumsController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Links
-{
- public sealed class PhotoAlbumsController : JsonApiController
- {
- public PhotoAlbumsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoLocation.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoLocation.cs
index 59857190..42d671fc 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoLocation.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoLocation.cs
@@ -4,8 +4,9 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Links
{
- [ResourceLinks(TopLevelLinks = LinkTypes.None, ResourceLinks = LinkTypes.None, RelationshipLinks = LinkTypes.Related)]
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [ResourceLinks(TopLevelLinks = LinkTypes.None, ResourceLinks = LinkTypes.None, RelationshipLinks = LinkTypes.Related)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Links")]
public sealed class PhotoLocation : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoLocationsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoLocationsController.cs
deleted file mode 100644
index c77e97d5..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoLocationsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Links
-{
- public sealed class PhotoLocationsController : JsonApiController
- {
- public PhotoLocationsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotosController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotosController.cs
deleted file mode 100644
index 0a3c83b9..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotosController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Links
-{
- public sealed class PhotosController : JsonApiController
- {
- public PhotosController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/AuditEntriesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/AuditEntriesController.cs
deleted file mode 100644
index d3104eb4..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/AuditEntriesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Logging
-{
- public sealed class AuditEntriesController : JsonApiController
- {
- public AuditEntriesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/AuditEntry.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/AuditEntry.cs
index 00aaa5c6..287478b9 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/AuditEntry.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Logging/AuditEntry.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Logging
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Logging")]
public sealed class AuditEntry : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/ProductFamiliesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/ProductFamiliesController.cs
deleted file mode 100644
index 408d6a34..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/ProductFamiliesController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Meta
-{
- public sealed class ProductFamiliesController : JsonApiController
- {
- public ProductFamiliesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/ProductFamily.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/ProductFamily.cs
index feb2d935..36b18a47 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/ProductFamily.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/ProductFamily.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Meta
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Meta")]
public sealed class ProductFamily : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/SupportTicket.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/SupportTicket.cs
index 603b6790..7d944b40 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/SupportTicket.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/SupportTicket.cs
@@ -5,6 +5,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Meta
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Meta")]
public sealed class SupportTicket : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/SupportTicketsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/SupportTicketsController.cs
deleted file mode 100644
index 9e5b6da6..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Meta/SupportTicketsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Meta
-{
- public sealed class SupportTicketsController : JsonApiController
- {
- public SupportTicketsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainGroup.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainGroup.cs
index f8028b65..33387c5c 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainGroup.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainGroup.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Microservices
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Microservices")]
public sealed class DomainGroup : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainGroupsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainGroupsController.cs
deleted file mode 100644
index cb1bbc6a..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainGroupsController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Microservices
-{
- public sealed class DomainGroupsController : JsonApiController
- {
- public DomainGroupsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainUser.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainUser.cs
index 6bc4af74..0af57e8a 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainUser.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainUser.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.Microservices
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Microservices")]
public sealed class DomainUser : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainUsersController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainUsersController.cs
deleted file mode 100644
index 3890ced6..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainUsersController.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.Microservices
-{
- public sealed class DomainUsersController : JsonApiController
- {
- public DomainUsersController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebProduct.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebProduct.cs
index 310aad6a..a2cf280b 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebProduct.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebProduct.cs
@@ -5,6 +5,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.MultiTenancy
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.MultiTenancy")]
public sealed class WebProduct : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebProductsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebProductsController.cs
index 53a46037..a21e576a 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebProductsController.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebProductsController.cs
@@ -1,20 +1,16 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Controllers.Annotations;
-using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
namespace JsonApiDotNetCoreTests.IntegrationTests.MultiTenancy
{
+ // Workaround for https://youtrack.jetbrains.com/issue/RSRP-487028
+ public partial class WebProductsController
+ {
+ }
+
[DisableRoutingConvention]
[Route("{countryCode}/products")]
- public sealed class WebProductsController : JsonApiController
+ partial class WebProductsController
{
- public WebProductsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
}
}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebShop.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebShop.cs
index c5830276..47441dfd 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebShop.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebShop.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.MultiTenancy
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.MultiTenancy")]
public sealed class WebShop : Identifiable, IHasTenant
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebShopsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebShopsController.cs
index 0907c67d..b532be8f 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebShopsController.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/MultiTenancy/WebShopsController.cs
@@ -1,20 +1,16 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Controllers.Annotations;
-using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
namespace JsonApiDotNetCoreTests.IntegrationTests.MultiTenancy
{
+ // Workaround for https://youtrack.jetbrains.com/issue/RSRP-487028
+ public partial class WebShopsController
+ {
+ }
+
[DisableRoutingConvention]
[Route("{countryCode}/shops")]
- public sealed class WebShopsController : JsonApiController
+ partial class WebShopsController
{
- public WebShopsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
}
}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResourcesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateKnownResourcesController.cs
similarity index 52%
rename from test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResourcesController.cs
rename to test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateKnownResourcesController.cs
index 82bb5972..80974043 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResourcesController.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateKnownResourcesController.cs
@@ -5,10 +5,10 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers
{
- public sealed class UnknownResourcesController : JsonApiController
+ public sealed class DuplicateKnownResourcesController : JsonApiController
{
- public UnknownResourcesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
+ public DuplicateKnownResourcesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
+ IResourceService resourceService)
: base(options, resourceGraph, loggerFactory, resourceService)
{
}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateResourceControllerTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateResourceControllerTests.cs
new file mode 100644
index 00000000..483fa7f2
--- /dev/null
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateResourceControllerTests.cs
@@ -0,0 +1,32 @@
+using System;
+using FluentAssertions;
+using JsonApiDotNetCore.Errors;
+using TestBuildingBlocks;
+using Xunit;
+
+namespace JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers
+{
+ public sealed class DuplicateResourceControllerTests : IntegrationTestContext, KnownDbContext>
+ {
+ public DuplicateResourceControllerTests()
+ {
+ UseController();
+ UseController();
+ }
+
+ [Fact]
+ public void Fails_at_startup_when_multiple_controllers_exist_for_same_resource_type()
+ {
+ // Act
+ Action action = () => _ = Factory;
+
+ // Assert
+ action.Should().ThrowExactly().WithMessage("Multiple controllers found for resource type 'knownResources'.");
+ }
+
+ public override void Dispose()
+ {
+ // Prevents crash when test cleanup tries to access lazily constructed Factory.
+ }
+ }
+}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/NonJsonApiDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/EmptyDbContext.cs
similarity index 64%
rename from test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/NonJsonApiDbContext.cs
rename to test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/EmptyDbContext.cs
index 5fb6d1d4..9f23ae9d 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/NonJsonApiDbContext.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/EmptyDbContext.cs
@@ -4,9 +4,9 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
- public sealed class NonJsonApiDbContext : DbContext
+ public sealed class EmptyDbContext : DbContext
{
- public NonJsonApiDbContext(DbContextOptions options)
+ public EmptyDbContext(DbContextOptions options)
: base(options)
{
}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownDbContext.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownDbContext.cs
new file mode 100644
index 00000000..86508c46
--- /dev/null
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownDbContext.cs
@@ -0,0 +1,16 @@
+using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore;
+
+namespace JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers
+{
+ [UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ public sealed class KnownDbContext : DbContext
+ {
+ public DbSet KnownResources => Set();
+
+ public KnownDbContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+ }
+}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownResource.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownResource.cs
new file mode 100644
index 00000000..739b824c
--- /dev/null
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownResource.cs
@@ -0,0 +1,13 @@
+using JetBrains.Annotations;
+using JsonApiDotNetCore.Resources;
+using JsonApiDotNetCore.Resources.Annotations;
+
+namespace JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers
+{
+ [UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers")]
+ public sealed class KnownResource : Identifiable
+ {
+ public string? Value { get; set; }
+ }
+}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs
index 1aa854b2..1fd1e8be 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/NonJsonApiControllerTests.cs
@@ -8,11 +8,11 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers
{
- public sealed class NonJsonApiControllerTests : IClassFixture, NonJsonApiDbContext>>
+ public sealed class NonJsonApiControllerTests : IClassFixture, EmptyDbContext>>
{
- private readonly IntegrationTestContext, NonJsonApiDbContext> _testContext;
+ private readonly IntegrationTestContext, EmptyDbContext> _testContext;
- public NonJsonApiControllerTests(IntegrationTestContext, NonJsonApiDbContext> testContext)
+ public NonJsonApiControllerTests(IntegrationTestContext, EmptyDbContext> testContext)
{
_testContext = testContext;
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResource.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResource.cs
index 5dc9f2f6..7a0b1e94 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResource.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResource.cs
@@ -1,9 +1,11 @@
using JetBrains.Annotations;
using JsonApiDotNetCore.Resources;
+using JsonApiDotNetCore.Resources.Annotations;
namespace JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers")]
public sealed class UnknownResource : Identifiable
{
public string? Value { get; set; }
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResourceControllerTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResourceControllerTests.cs
index d2803c19..d64d37b3 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResourceControllerTests.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/UnknownResourceControllerTests.cs
@@ -6,7 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.NonJsonApiControllers
{
- public sealed class UnknownResourceControllerTests : IntegrationTestContext, NonJsonApiDbContext>
+ public sealed class UnknownResourceControllerTests : IntegrationTestContext, EmptyDbContext>
{
public UnknownResourceControllerTests()
{
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Blog.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Blog.cs
index f421a0d6..58fb6830 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Blog.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Blog.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.QueryStrings")]
public sealed class Blog : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPost.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPost.cs
index 2e809d65..5542370f 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPost.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPost.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.QueryStrings")]
public sealed class BlogPost : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPostsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPostsController.cs
deleted file mode 100644
index b8eb194e..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPostsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings
-{
- public sealed class BlogPostsController : JsonApiController
- {
- public BlogPostsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogsController.cs
deleted file mode 100644
index 4fb4ad7e..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogsController.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings
-{
- public sealed class BlogsController : JsonApiController
- {
- public BlogsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Calendar.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Calendar.cs
index ba66badf..58857dea 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Calendar.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Calendar.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.QueryStrings")]
public sealed class Calendar : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/CalendarsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/CalendarsController.cs
deleted file mode 100644
index 64c0c106..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/CalendarsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings
-{
- public sealed class CalendarsController : JsonApiController
- {
- public CalendarsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Comment.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Comment.cs
index 1e0b6b5b..d059121e 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Comment.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Comment.cs
@@ -6,6 +6,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.QueryStrings")]
public sealed class Comment : Identifiable
{
[Attr]
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/CommentsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/CommentsController.cs
deleted file mode 100644
index b287d173..00000000
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/CommentsController.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using JsonApiDotNetCore.Configuration;
-using JsonApiDotNetCore.Controllers;
-using JsonApiDotNetCore.Services;
-using Microsoft.Extensions.Logging;
-
-namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings
-{
- public sealed class CommentsController : JsonApiController
- {
- public CommentsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory,
- IResourceService resourceService)
- : base(options, resourceGraph, loggerFactory, resourceService)
- {
- }
- }
-}
diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs
index 1ef672c0..aca906b1 100644
--- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs
+++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs
@@ -7,6 +7,7 @@
namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings.Filtering
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
+ [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.QueryStrings.Filtering")]
public sealed class FilterableResource : Identifiable