From 9c9a4f22ae5579a7a3c19adac42166a5097f49d1 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Fri, 3 Dec 2021 15:08:48 +0100 Subject: [PATCH] Auto-generated JSON:API controllers using source generators (#1117) * Removed Interface target from custom attributes, because it does not work (see https://stackoverflow.com/questions/540749/can-a-c-sharp-class-inherit-attributes-from-its-interface). Fixed detection of attribute usage on base classes. * Auto-generation of JSON:API controllers (using source generators) * Updated integration tests to use auto-generated controllers * Fixed: throw at startup when multiple controllers are registered for the same resource type * Addressed cleanupcode/inspectcode issues * Add dependency from JsonApiDotNetCore to SourceGenerators, so it gets pulled in via NuGet * Added unit tests for controller source generator * Update ROADMAP.md * Updated documentation * Produce NuGet package in cibuild This lets each project opt-in for producing a NuGet package, instead of listing them globally * Addressed review feedback --- Build.ps1 | 7 +- Directory.Build.props | 5 + JsonApiDotNetCore.sln | 69 +- README.md | 14 +- ROADMAP.md | 2 +- docs/getting-started/step-by-step.md | 18 +- docs/usage/extensibility/controllers.md | 119 ++- docs/usage/extensibility/services.md | 18 +- docs/usage/resource-graph.md | 18 +- docs/usage/routing.md | 29 +- .../Controllers/BooksController.cs | 16 - .../Controllers/PeopleController.cs | 17 - .../GettingStarted/GettingStarted.csproj | 1 + src/Examples/GettingStarted/Models/Book.cs | 1 + src/Examples/GettingStarted/Models/Person.cs | 1 + .../Controllers/PeopleController.cs | 17 - .../Controllers/TagsController.cs | 16 - .../Controllers/TodoItemsController.cs | 17 - .../JsonApiDotNetCoreExample.csproj | 2 + .../JsonApiDotNetCoreExample/Models/Person.cs | 1 + .../JsonApiDotNetCoreExample/Models/Tag.cs | 1 + .../Models/TodoItem.cs | 1 + .../Controllers/ResourceAsController.cs | 17 - .../Controllers/ResourceBsController.cs | 17 - .../MultiDbContextExample/Models/ResourceA.cs | 1 + .../MultiDbContextExample/Models/ResourceB.cs | 1 + .../MultiDbContextExample.csproj | 2 + .../Controllers/WorkItemsController.cs | 17 - .../Models/WorkItem.cs | 1 + .../NoEntityFrameworkExample.csproj | 2 + .../Controllers/ReportsController.cs | 26 - src/Examples/ReportsExample/Models/Report.cs | 2 + .../ReportsExample/ReportsExample.csproj | 2 + .../ControllerSourceGenerator.cs | 170 ++++ .../JsonApiDotNetCore.SourceGenerators.csproj | 53 ++ .../JsonApiEndpointsCopy.cs | 26 + .../Properties/launchSettings.json | 8 + .../SourceCodeWriter.cs | 275 +++++++ .../TypeWithAttributeSyntaxReceiver.cs | 41 + .../Configuration/ResourceGraphBuilder.cs | 8 +- .../Configuration/ResourceNameFormatter.cs | 6 +- .../DisableQueryStringAttribute.cs | 2 +- .../DisableRoutingConventionAttribute.cs | 2 +- .../Controllers/JsonApiEndpoints.cs | 31 + .../Errors/InvalidModelStateException.cs | 2 +- .../JsonApiDotNetCore.csproj | 13 +- .../AsyncQueryStringActionFilter.cs | 2 +- .../Middleware/JsonApiRoutingConvention.cs | 7 +- .../Annotations/ResourceAttribute.cs | 28 +- .../Annotations/ResourceLinksAttribute.cs | 2 +- .../Archiving/BroadcastComment.cs | 1 + .../Archiving/BroadcastCommentsController.cs | 16 - .../Archiving/TelevisionBroadcast.cs | 1 + .../TelevisionBroadcastsController.cs | 16 - .../Archiving/TelevisionNetwork.cs | 1 + .../Archiving/TelevisionNetworksController.cs | 16 - .../Archiving/TelevisionStation.cs | 1 + .../Archiving/TelevisionStationsController.cs | 16 - .../AtomicOperations/Lyric.cs | 1 + .../AtomicOperations/LyricsController.cs | 16 - .../AtomicOperations/MusicTrack.cs | 1 + .../AtomicOperations/MusicTracksController.cs | 17 - .../AtomicOperations/Performer.cs | 1 + .../AtomicOperations/PerformersController.cs | 16 - .../AtomicOperations/Playlist.cs | 1 + .../AtomicOperations/PlaylistsController.cs | 16 - .../RecordCompaniesController.cs | 16 - .../AtomicOperations/RecordCompany.cs | 1 + .../AtomicOperations/TextLanguage.cs | 1 + .../TextLanguagesController.cs | 17 - .../IntegrationTests/CompositeKeys/Car.cs | 1 + .../CompositeKeys/CarsController.cs | 16 - .../CompositeKeys/Dealership.cs | 1 + .../CompositeKeys/DealershipsController.cs | 16 - .../IntegrationTests/CompositeKeys/Engine.cs | 1 + .../CompositeKeys/EnginesController.cs | 16 - .../ContentNegotiation/PoliciesController.cs | 16 - .../ContentNegotiation/Policy.cs | 1 + .../ControllerActionResults/Toothbrush.cs | 1 + .../ToothbrushesController.cs | 17 +- .../IntegrationTests/CustomRoutes/Civilian.cs | 1 + .../CustomRoutes/CiviliansController.cs | 17 +- .../IntegrationTests/CustomRoutes/Town.cs | 1 + .../CustomRoutes/TownsController.cs | 10 +- .../IntegrationTests/EagerLoading/Building.cs | 1 + .../EagerLoading/BuildingsController.cs | 16 - .../IntegrationTests/EagerLoading/State.cs | 1 + .../EagerLoading/StatesController.cs | 16 - .../IntegrationTests/EagerLoading/Street.cs | 1 + .../EagerLoading/StreetsController.cs | 16 - .../ExceptionHandling/ConsumerArticle.cs | 1 + .../ConsumerArticlesController.cs | 16 - .../ExceptionHandling/ThrowingArticle.cs | 1 + .../ThrowingArticlesController.cs | 16 - .../HostingInIIS/ArtGalleriesController.cs | 16 - .../HostingInIIS/ArtGallery.cs | 1 + .../IntegrationTests/HostingInIIS/Painting.cs | 1 + .../HostingInIIS/PaintingsController.cs | 16 +- .../ModelState/SystemDirectoriesController.cs | 16 - .../ModelState/SystemDirectory.cs | 1 + .../InputValidation/ModelState/SystemFile.cs | 1 + .../ModelState/SystemFilesController.cs | 16 - .../ModelState/SystemVolume.cs | 1 + .../ModelState/SystemVolumesController.cs | 16 - .../InputValidation/RequestBody/Workflow.cs | 1 + .../RequestBody/WorkflowsController.cs | 17 - .../IntegrationTests/Links/Photo.cs | 1 + .../IntegrationTests/Links/PhotoAlbum.cs | 1 + .../Links/PhotoAlbumsController.cs | 17 - .../IntegrationTests/Links/PhotoLocation.cs | 3 +- .../Links/PhotoLocationsController.cs | 16 - .../Links/PhotosController.cs | 17 - .../Logging/AuditEntriesController.cs | 16 - .../IntegrationTests/Logging/AuditEntry.cs | 1 + .../Meta/ProductFamiliesController.cs | 16 - .../IntegrationTests/Meta/ProductFamily.cs | 1 + .../IntegrationTests/Meta/SupportTicket.cs | 1 + .../Meta/SupportTicketsController.cs | 16 - .../Microservices/DomainGroup.cs | 1 + .../Microservices/DomainGroupsController.cs | 17 - .../Microservices/DomainUser.cs | 1 + .../Microservices/DomainUsersController.cs | 17 - .../MultiTenancy/WebProduct.cs | 1 + .../MultiTenancy/WebProductsController.cs | 16 +- .../IntegrationTests/MultiTenancy/WebShop.cs | 1 + .../MultiTenancy/WebShopsController.cs | 16 +- ...s => DuplicateKnownResourcesController.cs} | 6 +- .../DuplicateResourceControllerTests.cs | 32 + ...nJsonApiDbContext.cs => EmptyDbContext.cs} | 4 +- .../NonJsonApiControllers/KnownDbContext.cs | 16 + .../NonJsonApiControllers/KnownResource.cs | 13 + .../NonJsonApiControllerTests.cs | 6 +- .../NonJsonApiControllers/UnknownResource.cs | 2 + .../UnknownResourceControllerTests.cs | 2 +- .../IntegrationTests/QueryStrings/Blog.cs | 1 + .../IntegrationTests/QueryStrings/BlogPost.cs | 1 + .../QueryStrings/BlogPostsController.cs | 16 - .../QueryStrings/BlogsController.cs | 15 - .../IntegrationTests/QueryStrings/Calendar.cs | 1 + .../QueryStrings/CalendarsController.cs | 16 - .../IntegrationTests/QueryStrings/Comment.cs | 1 + .../QueryStrings/CommentsController.cs | 16 - .../Filtering/FilterableResource.cs | 1 + .../FilterableResourcesController.cs | 16 - .../QueryStrings/WebAccount.cs | 1 + .../QueryStrings/WebAccountsController.cs | 16 - .../IntegrationTests/ReadWrite/RgbColor.cs | 1 + .../ReadWrite/RgbColorsController.cs | 16 - .../IntegrationTests/ReadWrite/UserAccount.cs | 1 + .../ReadWrite/UserAccountsController.cs | 16 - .../IntegrationTests/ReadWrite/WorkItem.cs | 1 + .../ReadWrite/WorkItemGroup.cs | 1 + .../ReadWrite/WorkItemGroupsController.cs | 17 - .../ReadWrite/WorkItemsController.cs | 16 - .../RequiredRelationships/Customer.cs | 1 + .../RequiredRelationships/Order.cs | 1 + .../RequiredRelationships/OrdersController.cs | 16 - .../RequiredRelationships/Shipment.cs | 1 + .../ShipmentsController.cs | 16 - .../GiftCertificate.cs | 1 + .../GiftCertificatesController.cs | 16 - .../PostOffice.cs | 1 + .../PostOfficesController.cs | 16 - .../ResourceDefinitions/Reading/Moon.cs | 1 + .../Reading/MoonsController.cs | 15 - .../ResourceDefinitions/Reading/Planet.cs | 1 + .../Reading/PlanetsController.cs | 16 - .../ResourceDefinitions/Reading/Star.cs | 1 + .../Reading/StarsController.cs | 15 - .../Serialization/Scholarship.cs | 1 + .../Serialization/ScholarshipsController.cs | 16 - .../Serialization/Student.cs | 1 + .../Serialization/StudentsController.cs | 16 - .../ResourceInheritance/Man.cs | 1 + .../ResourceInheritance/MenController.cs | 15 - .../RestrictedControllers/Bed.cs | 2 + .../BedsReadOnlyController.cs | 16 - .../RestrictedControllers/Chair.cs | 5 + .../ChairsNoRelationshipsController.cs | 17 - .../DisableQueryStringTests.cs | 4 +- .../NoRelationshipsControllerTests.cs | 2 +- .../RestrictedControllers/Pillow.cs | 1 + .../PillowsController.cs | 14 + .../PillowsNoSkipCacheController.cs | 18 - .../ReadOnlyControllerTests.cs | 2 +- .../RestrictedControllers/Sofa.cs | 1 + .../SofasBlockingSortPageController.cs | 19 - .../RestrictedControllers/SofasController.cs | 15 + .../RestrictedControllers/Table.cs | 2 + .../TablesWriteOnlyController.cs | 16 - .../WriteOnlyControllerTests.cs | 2 +- .../IntegrationTests/Serialization/Meeting.cs | 1 + .../Serialization/MeetingAttendee.cs | 1 + .../MeetingAttendeesController.cs | 17 - .../Serialization/MeetingsController.cs | 17 - .../SoftDeletion/CompaniesController.cs | 16 - .../IntegrationTests/SoftDeletion/Company.cs | 1 + .../SoftDeletion/Department.cs | 1 + .../SoftDeletion/DepartmentsController.cs | 16 - .../IntegrationTests/ZeroKeys/Game.cs | 1 + .../ZeroKeys/GamesController.cs | 16 - .../IntegrationTests/ZeroKeys/Map.cs | 1 + .../ZeroKeys/MapsController.cs | 16 - .../IntegrationTests/ZeroKeys/Player.cs | 1 + .../ZeroKeys/PlayersController.cs | 16 - .../JsonApiDotNetCoreTests.csproj | 2 + .../Controllers/ArticlesController.cs | 12 + .../Controllers}/CustomersController.cs | 9 +- .../JsonApiDotNetCore/ArgumentGuard.cs | 20 + .../JsonApiDotNetCore/AttrAttribute.cs | 17 + .../BaseJsonApiController.cs | 42 + .../IAddToRelationshipService.cs | 19 + .../JsonApiDotNetCore/ICreateService.cs | 19 + .../JsonApiDotNetCore/IDeleteService.cs | 19 + .../JsonApiDotNetCore/IGetAllService.cs | 19 + .../JsonApiDotNetCore/IGetByIdService.cs | 19 + .../IGetRelationshipService.cs | 19 + .../JsonApiDotNetCore/IGetSecondaryService.cs | 19 + .../JsonApiDotNetCore/IIdentifiable.cs | 24 + .../JsonApiDotNetCore/IJsonApiOptions.cs | 16 + .../IRemoveFromRelationshipService.cs | 19 + .../IResourceCommandService.cs | 20 + .../JsonApiDotNetCore/IResourceGraph.cs | 16 + .../IResourceQueryService.cs | 19 + .../ISetRelationshipService.cs | 19 + .../JsonApiDotNetCore/IUpdateService.cs | 19 + .../JsonApiDotNetCore/Identifiable.cs | 20 + .../JsonApiCommandController.cs | 26 + .../JsonApiDotNetCore/JsonApiController.cs | 37 + .../JsonApiDotNetCore/JsonApiOptions.cs | 17 + .../JsonApiQueryController.cs | 26 + .../JsonApiResourceService.cs | 18 + .../JsonApiDotNetCore/ResourceGraph.cs | 16 + .../ResourceServiceInterfaces.cs | 18 + .../SourceGeneratorDebugger/Models/Account.cs | 15 + .../SourceGeneratorDebugger/Models/Article.cs | 16 + .../Models/Customer.cs | 13 + test/SourceGeneratorDebugger/Models/Global.cs | 14 + test/SourceGeneratorDebugger/Models/Login.cs | 29 + test/SourceGeneratorDebugger/Models/Order.cs | 15 + .../Models/SimpleNamespace.cs | 16 + test/SourceGeneratorDebugger/Program.cs | 62 ++ .../SourceGeneratorDebugger.csproj | 21 + .../CompilationBuilder.cs | 79 ++ .../ControllerGenerationTests.cs | 776 ++++++++++++++++++ .../GeneratorDriverRunResultExtensions.cs | 77 ++ .../JsonApiEndpointsCopyTests.cs | 52 ++ .../SourceGeneratorTests/SourceCodeBuilder.cs | 65 ++ .../SourceGeneratorTests.csproj | 18 + test/TestBuildingBlocks/FakerContainer.cs | 2 +- 250 files changed, 2931 insertions(+), 1385 deletions(-) delete mode 100644 src/Examples/GettingStarted/Controllers/BooksController.cs delete mode 100644 src/Examples/GettingStarted/Controllers/PeopleController.cs delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Controllers/PeopleController.cs delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Controllers/TagsController.cs delete mode 100644 src/Examples/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs delete mode 100644 src/Examples/MultiDbContextExample/Controllers/ResourceAsController.cs delete mode 100644 src/Examples/MultiDbContextExample/Controllers/ResourceBsController.cs delete mode 100644 src/Examples/NoEntityFrameworkExample/Controllers/WorkItemsController.cs delete mode 100644 src/Examples/ReportsExample/Controllers/ReportsController.cs create mode 100644 src/JsonApiDotNetCore.SourceGenerators/ControllerSourceGenerator.cs create mode 100644 src/JsonApiDotNetCore.SourceGenerators/JsonApiDotNetCore.SourceGenerators.csproj create mode 100644 src/JsonApiDotNetCore.SourceGenerators/JsonApiEndpointsCopy.cs create mode 100644 src/JsonApiDotNetCore.SourceGenerators/Properties/launchSettings.json create mode 100644 src/JsonApiDotNetCore.SourceGenerators/SourceCodeWriter.cs create mode 100644 src/JsonApiDotNetCore.SourceGenerators/TypeWithAttributeSyntaxReceiver.cs create mode 100644 src/JsonApiDotNetCore/Controllers/JsonApiEndpoints.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/BroadcastCommentsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionBroadcastsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionNetworksController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Archiving/TelevisionStationsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/LyricsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/MusicTracksController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/PerformersController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/PlaylistsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/RecordCompaniesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/AtomicOperations/TextLanguagesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/DealershipsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/EnginesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ContentNegotiation/PoliciesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/BuildingsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/StatesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/StreetsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ConsumerArticlesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ExceptionHandling/ThrowingArticlesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/HostingInIIS/ArtGalleriesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemDirectoriesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemFilesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/ModelState/SystemVolumesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/WorkflowsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoAlbumsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotoLocationsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Links/PhotosController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Logging/AuditEntriesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Meta/ProductFamiliesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Meta/SupportTicketsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainGroupsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Microservices/DomainUsersController.cs rename test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/{UnknownResourcesController.cs => DuplicateKnownResourcesController.cs} (52%) create mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/DuplicateResourceControllerTests.cs rename test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/{NonJsonApiDbContext.cs => EmptyDbContext.cs} (64%) create mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownDbContext.cs create mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/NonJsonApiControllers/KnownResource.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogPostsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/BlogsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/CalendarsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/CommentsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterableResourcesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccountsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColorsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/UserAccountsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroupsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/OrdersController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/ShipmentsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/GiftCertificatesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/PostOfficesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/PlanetsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/StarsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/ScholarshipsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/StudentsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/MenController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/BedsReadOnlyController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/ChairsNoRelationshipsController.cs create mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/PillowsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/PillowsNoSkipCacheController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/SofasBlockingSortPageController.cs create mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/SofasController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/TablesWriteOnlyController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingAttendeesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/CompaniesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/DepartmentsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/GamesController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/MapsController.cs delete mode 100644 test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/PlayersController.cs create mode 100644 test/SourceGeneratorDebugger/Controllers/ArticlesController.cs rename test/{JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships => SourceGeneratorDebugger/Controllers}/CustomersController.cs (68%) create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/ArgumentGuard.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/AttrAttribute.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/BaseJsonApiController.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IAddToRelationshipService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/ICreateService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IDeleteService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetAllService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetByIdService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetRelationshipService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetSecondaryService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IIdentifiable.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IJsonApiOptions.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IRemoveFromRelationshipService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceCommandService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceGraph.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceQueryService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/ISetRelationshipService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/IUpdateService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/Identifiable.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiCommandController.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiController.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiOptions.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiQueryController.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiResourceService.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/ResourceGraph.cs create mode 100644 test/SourceGeneratorDebugger/JsonApiDotNetCore/ResourceServiceInterfaces.cs create mode 100644 test/SourceGeneratorDebugger/Models/Account.cs create mode 100644 test/SourceGeneratorDebugger/Models/Article.cs create mode 100644 test/SourceGeneratorDebugger/Models/Customer.cs create mode 100644 test/SourceGeneratorDebugger/Models/Global.cs create mode 100644 test/SourceGeneratorDebugger/Models/Login.cs create mode 100644 test/SourceGeneratorDebugger/Models/Order.cs create mode 100644 test/SourceGeneratorDebugger/Models/SimpleNamespace.cs create mode 100644 test/SourceGeneratorDebugger/Program.cs create mode 100644 test/SourceGeneratorDebugger/SourceGeneratorDebugger.csproj create mode 100644 test/SourceGeneratorTests/CompilationBuilder.cs create mode 100644 test/SourceGeneratorTests/ControllerGenerationTests.cs create mode 100644 test/SourceGeneratorTests/GeneratorDriverRunResultExtensions.cs create mode 100644 test/SourceGeneratorTests/JsonApiEndpointsCopyTests.cs create mode 100644 test/SourceGeneratorTests/SourceCodeBuilder.cs create mode 100644 test/SourceGeneratorTests/SourceGeneratorTests.csproj diff --git a/Build.ps1 b/Build.ps1 index b65fb5231a..e2e638fafa 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 895d25aaf5..da45eb941e 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 e5e97193c2..ade710c475 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 e9670ff393..5df6ed6b9f 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 49f499e79c..a1fc9267f3 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 21daf04171..697546561e 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 1993f77841..0c71f45090 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 77d772435e..5270f4bbd8 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 beb20d2d92..4232419bc6 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 c68914a04a..c4eb8ae0ba 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 3f049429cd..0000000000 --- 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 e7a5537f14..0000000000 --- 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 b28d7a0c5a..ffffc7b2ee 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 0957461cd7..1e1cb42b2b 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 f9b8e55fff..056d3b0522 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 0ebafd1767..0000000000 --- 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 b08af4e399..0000000000 --- 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 c862853302..0000000000 --- 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 95c1faf884..8e2baedf85 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 44be2df864..6814db7f1c 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 713eafe605..a620c31759 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 5c4d5c6ea1..872322b0b3 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 5fd3c662a4..0000000000 --- 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 33b89aa9ec..0000000000 --- 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 1c754be6ed..bed097b083 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 70941a1f4d..751b3859df 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 b28d7a0c5a..0216243bd6 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 055fa60ed8..0000000000 --- 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 083894fd04..a64feb70bb 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 f7c5aa19ec..14ef3721e7 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 8c177e7db0..0000000000 --- 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 65f6972d16..384cc36c94 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 95c1faf884..8e2baedf85 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 0000000000..6728fd537c --- /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 0000000000..c94885e7d9 --- /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 0000000000..14134adcfd --- /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 0000000000..2679b059a9 --- /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 0000000000..a5425931be --- /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 0000000000..0fbc18a758 --- /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 53e206cc0f..5228795326 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 c6be029b1b..4ce4b09afd 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 bfa842ed9a..f1d1077687 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 34dc0c97f4..93db0d7d0e 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 0000000000..5fa0c129d8 --- /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 6777412f4e..b9924d92d5 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 682a136adc..f69d39d64a 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 7a72874e5b..85ce5046af 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 d304fc9d1c..bd56c41123 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 501d085d57..4bb83a4e38 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 f4d3a1ffc7..3e513d7175 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 1a5e733de0..7c101a8ddd 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 d801d2eaa5..0000000000 --- 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 07c57183cc..a57efcfd03 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 d5cd933b56..0000000000 --- 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 fb4dafda39..70c12b6bac 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 4b981a8586..0000000000 --- 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 9f49047ddc..d929fd384e 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 4ab018ea8d..0000000000 --- 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 e39e52ffdd..ba3ced81b5 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 24936babb9..0000000000 --- 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 8e3071aeb7..e2e7710e76 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 697ba4a00e..0000000000 --- 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 d0403e340b..31f9d2f807 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 59c5dfc60c..0000000000 --- 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 43d05609a9..cc4d9cb458 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 1b893615f3..0000000000 --- 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 6cc63b23db..0000000000 --- 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 b8ab7be551..bf1abab6ab 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 4606858341..10733b4bf6 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 b2d2683313..0000000000 --- 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 9146bf865a..5e7b5cc9ad 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 aa0a2099be..0000000000 --- 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 42d11da754..651eeb692c 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 2ec7d85cda..0000000000 --- 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 2a322e7513..87e207c0a6 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 f995a72233..0000000000 --- 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 4edb2dfdec..0000000000 --- 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 27d850107c..9f8e8b2af9 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 674513f910..e79ba4c1c4 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 92bdf57157..fbebfa42b5 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 13141feca6..f0c67c80a0 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 aad9ccb421..b94f7b8989 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 ca893947a6..fd98d4c0c9 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 79b9cbc63b..3369ac7643 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 eb4a3d2fc7..519d62886c 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 f26107ba89..0000000000 --- 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 e15f5e2ee2..afd641b839 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 5e792adefd..0000000000 --- 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 d6aa1a97e3..736b0535f5 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 19aab24dda..0000000000 --- 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 4c914b05b5..f000a79217 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 c7e13af033..0000000000 --- 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 d0a3ce819a..210a2bb569 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 d518902f47..0000000000 --- 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 adec47514b..0000000000 --- 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 34bec33ba3..d6131d07c2 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 f2dd3082a4..52508dbdba 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 6dcc0d930b..3217f34511 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 0c4af4fa1a..0000000000 --- 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 549356dbca..caafbb66c1 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 7a8d796a58..c5143f344e 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 90fb26d246..0000000000 --- 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 b2c4ede226..2f1453cb13 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 a649619ec1..0000000000 --- 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 127d66355a..042fca1878 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 b31523b7e1..0000000000 --- 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 1ee92f0d58..a3b3e3b3ec 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 9f6df3c0d5..5e9ac09105 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 29f80bc733..0000000000 --- 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 5985719055..42d671fc13 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 c77e97d5e4..0000000000 --- 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 0a3c83b911..0000000000 --- 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 d3104eb45b..0000000000 --- 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 00aaa5c6b7..287478b9da 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 408d6a3414..0000000000 --- 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 feb2d9358a..36b18a47df 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 603b6790fd..7d944b407b 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 9e5b6da653..0000000000 --- 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 f8028b650e..33387c5cd4 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 cb1bbc6a32..0000000000 --- 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 6bc4af7483..0af57e8a49 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 3890ced6cb..0000000000 --- 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 310aad6a8b..a2cf280b0b 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 53a460376b..a21e576a59 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 c5830276d8..47441dfd8a 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 0907c67d25..b532be8f88 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 82bb597266..80974043d1 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 0000000000..483fa7f21a --- /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 5fb6d1d42e..9f23ae9da2 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 0000000000..86508c4655 --- /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 0000000000..739b824c9b --- /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 1aa854b281..1fd1e8be42 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 5dc9f2f6ce..7a0b1e9454 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 d2803c195c..d64d37b38a 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 f421a0d67c..58fb683086 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 2e809d651e..5542370f23 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 b8eb194e80..0000000000 --- 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 4fb4ad7eea..0000000000 --- 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 ba66badfad..58857dea5b 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 64c0c1060c..0000000000 --- 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 1e0b6b5b6e..d059121e04 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 b287d173fd..0000000000 --- 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 1ef672c059..aca906b1c1 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 { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterableResourcesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterableResourcesController.cs deleted file mode 100644 index 4ccd186fd5..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/Filtering/FilterableResourcesController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings.Filtering -{ - public sealed class FilterableResourcesController : JsonApiController - { - public FilterableResourcesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccount.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccount.cs index 21060d0eef..9810b59104 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccount.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccount.cs @@ -7,6 +7,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.QueryStrings { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.QueryStrings")] public sealed class WebAccount : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccountsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccountsController.cs deleted file mode 100644 index 52b54e19b3..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/WebAccountsController.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 WebAccountsController : JsonApiController - { - public WebAccountsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColor.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColor.cs index 741a189a9c..7691c4c30d 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColor.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColor.cs @@ -5,6 +5,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ReadWrite { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ReadWrite")] public sealed class RgbColor : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColorsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColorsController.cs deleted file mode 100644 index f84fcc75de..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/RgbColorsController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ReadWrite -{ - public sealed class RgbColorsController : JsonApiController - { - public RgbColorsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/UserAccount.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/UserAccount.cs index 0e739429b5..d4ca4c1609 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/UserAccount.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/UserAccount.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ReadWrite { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ReadWrite")] public sealed class UserAccount : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/UserAccountsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/UserAccountsController.cs deleted file mode 100644 index 61980968e8..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/UserAccountsController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ReadWrite -{ - public sealed class UserAccountsController : JsonApiController - { - public UserAccountsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs index 2cd0433676..1648c21d02 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItem.cs @@ -8,6 +8,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ReadWrite { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ReadWrite")] public sealed class WorkItem : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs index a1f66c0549..5f7479cbe8 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroup.cs @@ -8,6 +8,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ReadWrite { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ReadWrite")] public sealed class WorkItemGroup : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroupsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroupsController.cs deleted file mode 100644 index c9ce804450..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemGroupsController.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.ReadWrite -{ - public sealed class WorkItemGroupsController : JsonApiController - { - public WorkItemGroupsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemsController.cs deleted file mode 100644 index 3f069590ff..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/WorkItemsController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ReadWrite -{ - public sealed class WorkItemsController : JsonApiController - { - public WorkItemsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Customer.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Customer.cs index 01420de8dc..e96f0d2b86 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Customer.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Customer.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.RequiredRelationships { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.RequiredRelationships")] public sealed class Customer : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Order.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Order.cs index e371b25bb5..813ee5803a 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Order.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Order.cs @@ -5,6 +5,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.RequiredRelationships { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.RequiredRelationships")] public sealed class Order : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/OrdersController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/OrdersController.cs deleted file mode 100644 index 47c4d47d2a..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/OrdersController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.RequiredRelationships -{ - public sealed class OrdersController : JsonApiController - { - public OrdersController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Shipment.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Shipment.cs index aeef7e0cbf..57e2f4bfb4 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Shipment.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/Shipment.cs @@ -8,6 +8,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.RequiredRelationships { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.RequiredRelationships")] public sealed class Shipment : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/ShipmentsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/ShipmentsController.cs deleted file mode 100644 index 0bd68b5af6..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/ShipmentsController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.RequiredRelationships -{ - public sealed class ShipmentsController : JsonApiController - { - public ShipmentsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/GiftCertificate.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/GiftCertificate.cs index e6a5b7a0e2..d448059332 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/GiftCertificate.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/GiftCertificate.cs @@ -8,6 +8,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceConstructorInjection { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ResourceConstructorInjection")] public sealed class GiftCertificate : Identifiable { private readonly ISystemClock _systemClock; diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/GiftCertificatesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/GiftCertificatesController.cs deleted file mode 100644 index d51ceb542b..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/GiftCertificatesController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceConstructorInjection -{ - public sealed class GiftCertificatesController : JsonApiController - { - public GiftCertificatesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/PostOffice.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/PostOffice.cs index 5ca24b69b2..856b90c50b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/PostOffice.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/PostOffice.cs @@ -9,6 +9,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceConstructorInjection { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ResourceConstructorInjection")] public sealed class PostOffice : Identifiable { private readonly ISystemClock _systemClock; diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/PostOfficesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/PostOfficesController.cs deleted file mode 100644 index 5d0bbb5b0f..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceConstructorInjection/PostOfficesController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceConstructorInjection -{ - public sealed class PostOfficesController : JsonApiController - { - public PostOfficesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs index 67de51806a..16f2259953 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Moon.cs @@ -5,6 +5,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading")] public sealed class Moon : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonsController.cs deleted file mode 100644 index d498a8630b..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/MoonsController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading -{ - public sealed class MoonsController : JsonApiController - { - public MoonsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Planet.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Planet.cs index 21f196935a..89562d57dc 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Planet.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Planet.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading")] public sealed class Planet : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/PlanetsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/PlanetsController.cs deleted file mode 100644 index ec8a670592..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/PlanetsController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading -{ - public sealed class PlanetsController : JsonApiController - { - public PlanetsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Star.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Star.cs index b8d40ebde7..a73cf1fe5f 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Star.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/Star.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading")] public sealed class Star : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/StarsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/StarsController.cs deleted file mode 100644 index b1d661c207..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Reading/StarsController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Reading -{ - public sealed class StarsController : JsonApiController - { - public StarsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/Scholarship.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/Scholarship.cs index 60a0bb0170..1d1fb45f8e 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/Scholarship.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/Scholarship.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Serialization { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Serialization")] public sealed class Scholarship : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/ScholarshipsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/ScholarshipsController.cs deleted file mode 100644 index bd792e0cdf..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/ScholarshipsController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Serialization -{ - public sealed class ScholarshipsController : JsonApiController - { - public ScholarshipsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/Student.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/Student.cs index f6d0e5f97b..ccbddd5177 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/Student.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/Student.cs @@ -5,6 +5,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Serialization { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Serialization")] public sealed class Student : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/StudentsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/StudentsController.cs deleted file mode 100644 index 3cb63c97c4..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceDefinitions/Serialization/StudentsController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceDefinitions.Serialization -{ - public sealed class StudentsController : JsonApiController - { - public StudentsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/Man.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/Man.cs index 943a5df785..c77e3bde8b 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/Man.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/Man.cs @@ -4,6 +4,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceInheritance { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ResourceInheritance")] public sealed class Man : Human { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/MenController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/MenController.cs deleted file mode 100644 index 76026d6e58..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ResourceInheritance/MenController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ResourceInheritance -{ - public sealed class MenController : JsonApiController - { - public MenController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Bed.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Bed.cs index d197b4337e..abfc80fed5 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Bed.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Bed.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using JetBrains.Annotations; +using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(GenerateControllerEndpoints = JsonApiEndpoints.Query, ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers")] public sealed class Bed : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/BedsReadOnlyController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/BedsReadOnlyController.cs deleted file mode 100644 index 0b0aa04d45..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/BedsReadOnlyController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers -{ - public sealed class BedsReadOnlyController : JsonApiQueryController - { - public BedsReadOnlyController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceQueryService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Chair.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Chair.cs index 4e4a337b5b..ed07a9b39f 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Chair.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Chair.cs @@ -1,13 +1,18 @@ using System.Collections.Generic; using JetBrains.Annotations; +using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(GenerateControllerEndpoints = NoRelationshipEndpoints, ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers")] public sealed class Chair : Identifiable { + private const JsonApiEndpoints NoRelationshipEndpoints = JsonApiEndpoints.GetCollection | JsonApiEndpoints.GetSingle | JsonApiEndpoints.Post | + JsonApiEndpoints.Patch | JsonApiEndpoints.Delete; + [Attr] public int LegCount { get; set; } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/ChairsNoRelationshipsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/ChairsNoRelationshipsController.cs deleted file mode 100644 index 5b4bb83f3f..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/ChairsNoRelationshipsController.cs +++ /dev/null @@ -1,17 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers -{ - public sealed class ChairsNoRelationshipsController : JsonApiController - { - public ChairsNoRelationshipsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IGetAllService? getAll, IGetByIdService? getById, ICreateService? create, IUpdateService? update, - IDeleteService? delete) - : base(options, resourceGraph, loggerFactory, getAll, getById, null, null, create, null, update, null, delete) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs index 003a481928..6bd339e8d9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/DisableQueryStringTests.cs @@ -18,8 +18,8 @@ public DisableQueryStringTests(IntegrationTestContext(); - testContext.UseController(); + testContext.UseController(); + testContext.UseController(); testContext.ConfigureServicesAfterStartup(services => { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/NoRelationshipsControllerTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/NoRelationshipsControllerTests.cs index 191238463f..4fb7c8f119 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/NoRelationshipsControllerTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/NoRelationshipsControllerTests.cs @@ -18,7 +18,7 @@ public NoRelationshipsControllerTests(IntegrationTestContext(); + testContext.UseController(); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Pillow.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Pillow.cs index cf3642aeaf..ee2512bdb2 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Pillow.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Pillow.cs @@ -5,6 +5,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers")] public sealed class Pillow : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/PillowsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/PillowsController.cs new file mode 100644 index 0000000000..7f1bef48e8 --- /dev/null +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/PillowsController.cs @@ -0,0 +1,14 @@ +using JsonApiDotNetCore.Controllers.Annotations; + +namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers +{ + // Workaround for https://youtrack.jetbrains.com/issue/RSRP-487028 + public partial class PillowsController + { + } + + [DisableQueryString("skipCache")] + partial class PillowsController + { + } +} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/PillowsNoSkipCacheController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/PillowsNoSkipCacheController.cs deleted file mode 100644 index 2ca39b0072..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/PillowsNoSkipCacheController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Controllers.Annotations; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers -{ - [DisableQueryString("skipCache")] - public sealed class PillowsNoSkipCacheController : JsonApiController - { - public PillowsNoSkipCacheController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/ReadOnlyControllerTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/ReadOnlyControllerTests.cs index d171982473..b612228075 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/ReadOnlyControllerTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/ReadOnlyControllerTests.cs @@ -18,7 +18,7 @@ public ReadOnlyControllerTests(IntegrationTestContext(); + testContext.UseController(); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Sofa.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Sofa.cs index c110b5d0d8..87124b7df8 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Sofa.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Sofa.cs @@ -5,6 +5,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers")] public sealed class Sofa : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/SofasBlockingSortPageController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/SofasBlockingSortPageController.cs deleted file mode 100644 index 7f300a3e0a..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/SofasBlockingSortPageController.cs +++ /dev/null @@ -1,19 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Controllers.Annotations; -using JsonApiDotNetCore.QueryStrings; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers -{ - [DisableQueryString(JsonApiQueryStringParameters.Sort | JsonApiQueryStringParameters.Page)] - public sealed class SofasBlockingSortPageController : JsonApiController - { - public SofasBlockingSortPageController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/SofasController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/SofasController.cs new file mode 100644 index 0000000000..ca181aee96 --- /dev/null +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/SofasController.cs @@ -0,0 +1,15 @@ +using JsonApiDotNetCore.Controllers.Annotations; +using JsonApiDotNetCore.QueryStrings; + +namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers +{ + // Workaround for https://youtrack.jetbrains.com/issue/RSRP-487028 + public partial class SofasController + { + } + + [DisableQueryString(JsonApiQueryStringParameters.Sort | JsonApiQueryStringParameters.Page)] + partial class SofasController + { + } +} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Table.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Table.cs index c0df183f38..a674e212d0 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Table.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/Table.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using JetBrains.Annotations; +using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(GenerateControllerEndpoints = JsonApiEndpoints.Command, ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers")] public sealed class Table : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/TablesWriteOnlyController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/TablesWriteOnlyController.cs deleted file mode 100644 index 484e8fc13a..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/TablesWriteOnlyController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.RestrictedControllers -{ - public sealed class TablesWriteOnlyController : JsonApiCommandController - { - public TablesWriteOnlyController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceCommandService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/WriteOnlyControllerTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/WriteOnlyControllerTests.cs index 7008eafd9a..c91be6541c 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/WriteOnlyControllerTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/RestrictedControllers/WriteOnlyControllerTests.cs @@ -18,7 +18,7 @@ public WriteOnlyControllerTests(IntegrationTestContext(); + testContext.UseController(); } [Fact] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/Meeting.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/Meeting.cs index 7e35498f92..f1decfff3a 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/Meeting.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/Meeting.cs @@ -9,6 +9,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.Serialization { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Serialization")] public sealed class Meeting : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingAttendee.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingAttendee.cs index 76d3f17b3b..3054577dc6 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingAttendee.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingAttendee.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.Serialization { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.Serialization")] public sealed class MeetingAttendee : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingAttendeesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingAttendeesController.cs deleted file mode 100644 index bc3e213001..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingAttendeesController.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.Serialization -{ - public sealed class MeetingAttendeesController : JsonApiController - { - public MeetingAttendeesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingsController.cs deleted file mode 100644 index f21b312e42..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/Serialization/MeetingsController.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.Serialization -{ - public sealed class MeetingsController : JsonApiController - { - public MeetingsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/CompaniesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/CompaniesController.cs deleted file mode 100644 index fab829e553..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/CompaniesController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.SoftDeletion -{ - public sealed class CompaniesController : JsonApiController - { - public CompaniesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/Company.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/Company.cs index 9246a523ab..1f6115ef6f 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/Company.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/Company.cs @@ -7,6 +7,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.SoftDeletion { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.SoftDeletion")] public sealed class Company : Identifiable, ISoftDeletable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/Department.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/Department.cs index 26f85f4026..89623927eb 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/Department.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/Department.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.SoftDeletion { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.SoftDeletion")] public sealed class Department : Identifiable, ISoftDeletable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/DepartmentsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/DepartmentsController.cs deleted file mode 100644 index b3302da92d..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/SoftDeletion/DepartmentsController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.SoftDeletion -{ - public sealed class DepartmentsController : JsonApiController - { - public DepartmentsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Game.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Game.cs index afe1365d16..008b89ffc7 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Game.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Game.cs @@ -8,6 +8,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ZeroKeys { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ZeroKeys")] public sealed class Game : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/GamesController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/GamesController.cs deleted file mode 100644 index 8497e9c91e..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/GamesController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ZeroKeys -{ - public sealed class GamesController : JsonApiController - { - public GamesController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Map.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Map.cs index bf17d26b1c..2c2a07f466 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Map.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Map.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ZeroKeys { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ZeroKeys")] public sealed class Map : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/MapsController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/MapsController.cs deleted file mode 100644 index af38cf0a92..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/MapsController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ZeroKeys -{ - public sealed class MapsController : JsonApiController - { - public MapsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Player.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Player.cs index d83d14ddc2..d1b166a59c 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Player.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/Player.cs @@ -6,6 +6,7 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.ZeroKeys { [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.ZeroKeys")] public sealed class Player : Identifiable { [Attr] diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/PlayersController.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/PlayersController.cs deleted file mode 100644 index d9c9ebd302..0000000000 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ZeroKeys/PlayersController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Controllers; -using JsonApiDotNetCore.Services; -using Microsoft.Extensions.Logging; - -namespace JsonApiDotNetCoreTests.IntegrationTests.ZeroKeys -{ - public sealed class PlayersController : JsonApiController - { - public PlayersController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) - : base(options, resourceGraph, loggerFactory, resourceService) - { - } - } -} diff --git a/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj b/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj index 742c8a7674..9f51c80f50 100644 --- a/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj +++ b/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj @@ -11,6 +11,8 @@ + diff --git a/test/SourceGeneratorDebugger/Controllers/ArticlesController.cs b/test/SourceGeneratorDebugger/Controllers/ArticlesController.cs new file mode 100644 index 0000000000..77d073df90 --- /dev/null +++ b/test/SourceGeneratorDebugger/Controllers/ArticlesController.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; + +namespace SourceGeneratorDebugger.Controllers +{ + [PublicAPI] + partial class ArticlesController + { + public void ExtraMethod() + { + } + } +} diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/CustomersController.cs b/test/SourceGeneratorDebugger/Controllers/CustomersController.cs similarity index 68% rename from test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/CustomersController.cs rename to test/SourceGeneratorDebugger/Controllers/CustomersController.cs index c580d90075..99d9edf1e5 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/RequiredRelationships/CustomersController.cs +++ b/test/SourceGeneratorDebugger/Controllers/CustomersController.cs @@ -1,14 +1,17 @@ +using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Services; using Microsoft.Extensions.Logging; +using SourceGeneratorDebugger.Models; -namespace JsonApiDotNetCoreTests.IntegrationTests.RequiredRelationships +namespace SourceGeneratorDebugger.Controllers { - public sealed class CustomersController : JsonApiController + [PublicAPI] + public sealed class CustomersController : JsonApiController { public CustomersController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, - IResourceService resourceService) + IResourceService resourceService) : base(options, resourceGraph, loggerFactory, resourceService) { } diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/ArgumentGuard.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/ArgumentGuard.cs new file mode 100644 index 0000000000..27b864d2a4 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/ArgumentGuard.cs @@ -0,0 +1,20 @@ +using JetBrains.Annotations; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name +#pragma warning disable AV1008 // Class should not be static + +namespace JsonApiDotNetCore +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + internal static class ArgumentGuard + { + public static void NotNullNorEmpty(string? value, string name) + { + } + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/AttrAttribute.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/AttrAttribute.cs new file mode 100644 index 0000000000..90abf25d79 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/AttrAttribute.cs @@ -0,0 +1,17 @@ +using System; +using JetBrains.Annotations; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Resources.Annotations +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public sealed class AttrAttribute : Attribute + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/BaseJsonApiController.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/BaseJsonApiController.cs new file mode 100644 index 0000000000..08a752c9e3 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/BaseJsonApiController.cs @@ -0,0 +1,42 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Controllers +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public abstract class BaseJsonApiController + where TResource : class, IIdentifiable + { + protected BaseJsonApiController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceService resourceService) + : this(options, resourceGraph, loggerFactory, resourceService, resourceService) + { + } + + protected BaseJsonApiController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceQueryService? queryService = null, IResourceCommandService? commandService = null) + : this(options, resourceGraph, loggerFactory, queryService, queryService, queryService, queryService, commandService, commandService, + commandService, commandService, commandService, commandService) + { + } + + protected BaseJsonApiController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IGetAllService? getAll = null, IGetByIdService? getById = null, + IGetSecondaryService? getSecondary = null, IGetRelationshipService? getRelationship = null, + ICreateService? create = null, IAddToRelationshipService? addToRelationship = null, + IUpdateService? update = null, ISetRelationshipService? setRelationship = null, + IDeleteService? delete = null, IRemoveFromRelationshipService? removeFromRelationship = null) + { + } + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IAddToRelationshipService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IAddToRelationshipService.cs new file mode 100644 index 0000000000..9002eb06d3 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IAddToRelationshipService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable UnusedTypeParameter +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IAddToRelationshipService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/ICreateService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/ICreateService.cs new file mode 100644 index 0000000000..475574e731 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/ICreateService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable UnusedTypeParameter +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface ICreateService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IDeleteService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IDeleteService.cs new file mode 100644 index 0000000000..3ac9e91bf5 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IDeleteService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable UnusedTypeParameter +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IDeleteService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetAllService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetAllService.cs new file mode 100644 index 0000000000..8b484fd146 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetAllService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable UnusedTypeParameter +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IGetAllService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetByIdService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetByIdService.cs new file mode 100644 index 0000000000..d3a0d29344 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetByIdService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable UnusedTypeParameter +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IGetByIdService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetRelationshipService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetRelationshipService.cs new file mode 100644 index 0000000000..a3375bb6e5 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetRelationshipService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable UnusedTypeParameter +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IGetRelationshipService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetSecondaryService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetSecondaryService.cs new file mode 100644 index 0000000000..d1ee143ddc --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IGetSecondaryService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable UnusedTypeParameter +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IGetSecondaryService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IIdentifiable.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IIdentifiable.cs new file mode 100644 index 0000000000..b744155709 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IIdentifiable.cs @@ -0,0 +1,24 @@ +using JetBrains.Annotations; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Resources +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IIdentifiable + { + string? StringId { get; set; } + string? LocalId { get; set; } + } + + [PublicAPI] + public interface IIdentifiable : IIdentifiable + { + TId Id { get; set; } + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IJsonApiOptions.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IJsonApiOptions.cs new file mode 100644 index 0000000000..eba49836e1 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IJsonApiOptions.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Configuration +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IJsonApiOptions + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IRemoveFromRelationshipService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IRemoveFromRelationshipService.cs new file mode 100644 index 0000000000..c5da68bb41 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IRemoveFromRelationshipService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable UnusedTypeParameter +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IRemoveFromRelationshipService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceCommandService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceCommandService.cs new file mode 100644 index 0000000000..56cf59dc55 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceCommandService.cs @@ -0,0 +1,20 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IResourceCommandService + : ICreateService, IAddToRelationshipService, IUpdateService, ISetRelationshipService, + IDeleteService, IRemoveFromRelationshipService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceGraph.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceGraph.cs new file mode 100644 index 0000000000..554317425d --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceGraph.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Configuration +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IResourceGraph + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceQueryService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceQueryService.cs new file mode 100644 index 0000000000..fca4ddd215 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IResourceQueryService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IResourceQueryService + : IGetAllService, IGetByIdService, IGetRelationshipService, IGetSecondaryService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/ISetRelationshipService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/ISetRelationshipService.cs new file mode 100644 index 0000000000..4d4b396740 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/ISetRelationshipService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable UnusedTypeParameter +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface ISetRelationshipService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/IUpdateService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IUpdateService.cs new file mode 100644 index 0000000000..a6c7f9659b --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/IUpdateService.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable UnusedTypeParameter +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IUpdateService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/Identifiable.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/Identifiable.cs new file mode 100644 index 0000000000..52a2e5ef75 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/Identifiable.cs @@ -0,0 +1,20 @@ +using JetBrains.Annotations; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Resources +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public abstract class Identifiable : IIdentifiable + { + public virtual TId Id { get; set; } = default!; + + public string? StringId { get; set; } + public string? LocalId { get; set; } + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiCommandController.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiCommandController.cs new file mode 100644 index 0000000000..dc25d63fb1 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiCommandController.cs @@ -0,0 +1,26 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Controllers +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public abstract class JsonApiCommandController : BaseJsonApiController + where TResource : class, IIdentifiable + { + protected JsonApiCommandController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceCommandService commandService) + : base(options, resourceGraph, loggerFactory, null, commandService) + { + } + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiController.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiController.cs new file mode 100644 index 0000000000..6d582b08e0 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiController.cs @@ -0,0 +1,37 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Controllers +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public abstract class JsonApiController : BaseJsonApiController + where TResource : class, IIdentifiable + { + protected JsonApiController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, resourceGraph, loggerFactory, resourceService) + { + } + + protected JsonApiController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IGetAllService? getAll = null, IGetByIdService? getById = null, + IGetSecondaryService? getSecondary = null, IGetRelationshipService? getRelationship = null, + ICreateService? create = null, IAddToRelationshipService? addToRelationship = null, + IUpdateService? update = null, ISetRelationshipService? setRelationship = null, + IDeleteService? delete = null, IRemoveFromRelationshipService? removeFromRelationship = null) + : base(options, resourceGraph, loggerFactory, getAll, getById, getSecondary, getRelationship, create, addToRelationship, update, setRelationship, + delete, removeFromRelationship) + { + } + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiOptions.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiOptions.cs new file mode 100644 index 0000000000..9a7cdff0f0 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiOptions.cs @@ -0,0 +1,17 @@ +// ReSharper disable CheckNamespace + +using JetBrains.Annotations; + +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Configuration +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public sealed class JsonApiOptions : IJsonApiOptions + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiQueryController.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiQueryController.cs new file mode 100644 index 0000000000..c77b660e85 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiQueryController.cs @@ -0,0 +1,26 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Controllers +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public abstract class JsonApiQueryController : BaseJsonApiController + where TResource : class, IIdentifiable + { + protected JsonApiQueryController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceQueryService queryService) + : base(options, resourceGraph, loggerFactory, queryService) + { + } + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiResourceService.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiResourceService.cs new file mode 100644 index 0000000000..8fff1421f8 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/JsonApiResourceService.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public sealed class JsonApiResourceService : IResourceService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/ResourceGraph.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/ResourceGraph.cs new file mode 100644 index 0000000000..e0113e4685 --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/ResourceGraph.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Configuration +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public sealed class ResourceGraph : IResourceGraph + { + } +} diff --git a/test/SourceGeneratorDebugger/JsonApiDotNetCore/ResourceServiceInterfaces.cs b/test/SourceGeneratorDebugger/JsonApiDotNetCore/ResourceServiceInterfaces.cs new file mode 100644 index 0000000000..ae05e9334a --- /dev/null +++ b/test/SourceGeneratorDebugger/JsonApiDotNetCore/ResourceServiceInterfaces.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace JsonApiDotNetCore.Services +{ + /// + /// Represents a stripped-down copy of this type in the JsonApiDotNetCore project. It exists solely to fulfill the dependency needs for successfully + /// compiling the source-generated controllers in this project. + /// + [PublicAPI] + public interface IResourceService : IResourceCommandService, IResourceQueryService + where TResource : class, IIdentifiable + { + } +} diff --git a/test/SourceGeneratorDebugger/Models/Account.cs b/test/SourceGeneratorDebugger/Models/Account.cs new file mode 100644 index 0000000000..514c2acfb6 --- /dev/null +++ b/test/SourceGeneratorDebugger/Models/Account.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace SourceGeneratorDebugger.Models +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(GenerateControllerEndpoints = JsonApiEndpoints.Query, ControllerNamespace = "Some.Namespace.To.Place.Controllers")] + public sealed class Account : Identifiable + { + [Attr] + public string? DisplayName { get; set; } + } +} diff --git a/test/SourceGeneratorDebugger/Models/Article.cs b/test/SourceGeneratorDebugger/Models/Article.cs new file mode 100644 index 0000000000..aad473f069 --- /dev/null +++ b/test/SourceGeneratorDebugger/Models/Article.cs @@ -0,0 +1,16 @@ +using System; +using JetBrains.Annotations; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace SourceGeneratorDebugger.Models +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(GenerateControllerEndpoints = JsonApiEndpoints.GetCollection | JsonApiEndpoints.GetSingle | JsonApiEndpoints.GetSecondary)] + public sealed class Article : Identifiable + { + [Attr] + public string? DisplayName { get; set; } + } +} diff --git a/test/SourceGeneratorDebugger/Models/Customer.cs b/test/SourceGeneratorDebugger/Models/Customer.cs new file mode 100644 index 0000000000..007879f4e3 --- /dev/null +++ b/test/SourceGeneratorDebugger/Models/Customer.cs @@ -0,0 +1,13 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace SourceGeneratorDebugger.Models +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + public sealed class Customer : Identifiable + { + [Attr] + public string Name { get; set; } = null!; + } +} diff --git a/test/SourceGeneratorDebugger/Models/Global.cs b/test/SourceGeneratorDebugger/Models/Global.cs new file mode 100644 index 0000000000..ef62b974ca --- /dev/null +++ b/test/SourceGeneratorDebugger/Models/Global.cs @@ -0,0 +1,14 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +// ReSharper disable CheckNamespace +#pragma warning disable AV1505 // Namespace should match with assembly name + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource] +public sealed class Global : Identifiable +{ + [Attr] + public string? Value { get; set; } +} diff --git a/test/SourceGeneratorDebugger/Models/Login.cs b/test/SourceGeneratorDebugger/Models/Login.cs new file mode 100644 index 0000000000..d88ac7ff6b --- /dev/null +++ b/test/SourceGeneratorDebugger/Models/Login.cs @@ -0,0 +1,29 @@ +using System; +using JetBrains.Annotations; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +#pragma warning disable AV1505 // Namespace should match with assembly name + +namespace SourceGeneratorDebugger.Models +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(GenerateControllerEndpoints = JsonApiEndpoints.Command)] + public sealed class Login : Identifiable + { + [Attr] + public DateTimeOffset Time { get; set; } + } +} + +namespace Some.Other.Path +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(GenerateControllerEndpoints = JsonApiEndpoints.Command)] + public sealed class Login : Identifiable + { + [Attr] + public DateTimeOffset Time { get; set; } + } +} diff --git a/test/SourceGeneratorDebugger/Models/Order.cs b/test/SourceGeneratorDebugger/Models/Order.cs new file mode 100644 index 0000000000..6eab915213 --- /dev/null +++ b/test/SourceGeneratorDebugger/Models/Order.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace SourceGeneratorDebugger.Models +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource(GenerateControllerEndpoints = JsonApiEndpoints.All)] + public sealed class Order : Identifiable + { + [Attr] + public decimal TotalAmount { get; set; } + } +} diff --git a/test/SourceGeneratorDebugger/Models/SimpleNamespace.cs b/test/SourceGeneratorDebugger/Models/SimpleNamespace.cs new file mode 100644 index 0000000000..498364eedc --- /dev/null +++ b/test/SourceGeneratorDebugger/Models/SimpleNamespace.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +// ReSharper disable CheckNamespace + +namespace SourceGeneratorDebugger +{ + [UsedImplicitly(ImplicitUseTargetFlags.Members)] + [Resource] + public sealed class SimpleNamespace : Identifiable + { + [Attr] + public string? Value { get; set; } + } +} diff --git a/test/SourceGeneratorDebugger/Program.cs b/test/SourceGeneratorDebugger/Program.cs new file mode 100644 index 0000000000..464cb5b289 --- /dev/null +++ b/test/SourceGeneratorDebugger/Program.cs @@ -0,0 +1,62 @@ +using System; +using Controllers; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Services; +using Microsoft.Extensions.Logging.Abstractions; +using Some.Namespace.To.Place.Controllers; +using SourceGeneratorDebugger.Controllers; +using SourceGeneratorDebugger.Models; + +namespace SourceGeneratorDebugger +{ + // Until https://github.com/dotnet/roslyn/issues/55802 is fixed, this project enables us to debug the ASP.NET Controller source generator. + // In Visual Studio, set JsonApiDotNetCore.SourceGenerators as startup project, add a breakpoint at the start of ControllerSourceGenerator and press F5. + internal static class Program + { + public static void Main() + { + JsonApiOptions options = new(); + ResourceGraph resourceGraph = new(); + + // Built-in + IResourceService customerResourceService = new JsonApiResourceService(); + CustomersController customersController = new(options, resourceGraph, NullLoggerFactory.Instance, customerResourceService); + GC.KeepAlive(customersController); + + // Generated + IResourceService orderResourceService = new JsonApiResourceService(); + OrdersController ordersController = new(options, resourceGraph, NullLoggerFactory.Instance, orderResourceService); + GC.KeepAlive(ordersController); + + // Generated Query + IResourceQueryService accountQueryService = new JsonApiResourceService(); + AccountsController accountsController = new(options, resourceGraph, NullLoggerFactory.Instance, accountQueryService); + GC.KeepAlive(accountsController); + + // Generated Command + IResourceCommandService loginCommandService = new JsonApiResourceService(); + LoginsController loginsController = new(options, resourceGraph, NullLoggerFactory.Instance, loginCommandService); + GC.KeepAlive(loginsController); + + // Generated mix + IGetAllService articleGetAllResourceService = new JsonApiResourceService(); + IGetByIdService articleGetByIdResourceService = new JsonApiResourceService(); + IGetSecondaryService articleGetSecondaryResourceService = new JsonApiResourceService(); + + ArticlesController articlesController = new(options, resourceGraph, NullLoggerFactory.Instance, articleGetAllResourceService, + articleGetByIdResourceService, articleGetSecondaryResourceService); + + articlesController.ExtraMethod(); + + // Generated in global namespace + IResourceService globalResourceService = new JsonApiResourceService(); + GlobalsController globalsController = new(options, resourceGraph, NullLoggerFactory.Instance, globalResourceService); + GC.KeepAlive(globalsController); + + // Generated in non-nested namespace + IResourceService singleNamespaceResourceService = new JsonApiResourceService(); + SimpleNamespacesController singleNamespacesController = new(options, resourceGraph, NullLoggerFactory.Instance, singleNamespaceResourceService); + GC.KeepAlive(singleNamespacesController); + } + } +} diff --git a/test/SourceGeneratorDebugger/SourceGeneratorDebugger.csproj b/test/SourceGeneratorDebugger/SourceGeneratorDebugger.csproj new file mode 100644 index 0000000000..316294023b --- /dev/null +++ b/test/SourceGeneratorDebugger/SourceGeneratorDebugger.csproj @@ -0,0 +1,21 @@ + + + $(NetCoreAppVersion) + Exe + true + $(BaseIntermediateOutputPath)\GeneratedFiles + + + + + + + + + + + + + + + diff --git a/test/SourceGeneratorTests/CompilationBuilder.cs b/test/SourceGeneratorTests/CompilationBuilder.cs new file mode 100644 index 0000000000..e4cd8b045a --- /dev/null +++ b/test/SourceGeneratorTests/CompilationBuilder.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.IO; +using JsonApiDotNetCore.Resources.Annotations; +using Microsoft.AspNetCore.Mvc; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.Extensions.Logging; + +namespace SourceGeneratorTests +{ + internal sealed class CompilationBuilder + { + private static readonly CSharpCompilationOptions DefaultOptions = new(OutputKind.DynamicallyLinkedLibrary); + + private readonly HashSet _syntaxTrees = new(); + private readonly HashSet _references = new(); + + public Compilation Build() + { + return CSharpCompilation.Create("compilation", _syntaxTrees, _references, DefaultOptions); + } + + public CompilationBuilder WithDefaultReferences() + { + WithSystemReferences(); + WithLoggerFactoryReference(); + WithJsonApiDotNetCoreReferences(); + return this; + } + + public CompilationBuilder WithSystemReferences() + { + string objectLocation = typeof(object).Assembly.Location; + + PortableExecutableReference systemRuntimeReference = + MetadataReference.CreateFromFile(Path.Combine(Path.GetDirectoryName(objectLocation)!, "System.Runtime.dll")); + + foreach (var reference in new[] + { + MetadataReference.CreateFromFile(objectLocation), + systemRuntimeReference + }) + { + _references.Add(reference); + } + + return this; + } + + public CompilationBuilder WithLoggerFactoryReference() + { + PortableExecutableReference loggerFactoryReference = MetadataReference.CreateFromFile(typeof(ILoggerFactory).Assembly.Location); + _references.Add(loggerFactoryReference); + + return this; + } + + public CompilationBuilder WithJsonApiDotNetCoreReferences() + { + foreach (var reference in new[] + { + MetadataReference.CreateFromFile(typeof(ControllerBase).Assembly.Location), + MetadataReference.CreateFromFile(typeof(AttrAttribute).Assembly.Location) + }) + { + _references.Add(reference); + } + + return this; + } + + public CompilationBuilder WithSourceCode(string source) + { + SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source); + _syntaxTrees.Add(syntaxTree); + return this; + } + } +} diff --git a/test/SourceGeneratorTests/ControllerGenerationTests.cs b/test/SourceGeneratorTests/ControllerGenerationTests.cs new file mode 100644 index 0000000000..819364a990 --- /dev/null +++ b/test/SourceGeneratorTests/ControllerGenerationTests.cs @@ -0,0 +1,776 @@ +using FluentAssertions; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; +using JsonApiDotNetCore.SourceGenerators; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Xunit; + +namespace SourceGeneratorTests +{ + public sealed class ControllerGenerationTests + { + [Fact] + public void Can_generate_for_default_controller() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .InNamespace("ExampleApi.Models") + .WithCode(@" + [Resource] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + + runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using ExampleApi.Models; + +namespace ExampleApi.Controllers +{ + public sealed partial class ItemsController : JsonApiController + { + public ItemsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, resourceGraph, loggerFactory, resourceService) + { + } + } +} +"); + } + + [Fact] + public void Can_generate_for_read_only_controller() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .WithNamespaceImportFor(typeof(JsonApiEndpoints)) + .InNamespace("ExampleApi.Models") + .WithCode(@" + [Resource(GenerateControllerEndpoints = JsonApiEndpoints.Query)] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + + runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using ExampleApi.Models; + +namespace ExampleApi.Controllers +{ + public sealed partial class ItemsController : JsonApiQueryController + { + public ItemsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceQueryService queryService) + : base(options, resourceGraph, loggerFactory, queryService) + { + } + } +} +"); + } + + [Fact] + public void Can_generate_for_write_only_controller() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .WithNamespaceImportFor(typeof(JsonApiEndpoints)) + .InNamespace("ExampleApi.Models") + .WithCode(@" + [Resource(GenerateControllerEndpoints = JsonApiEndpoints.Command)] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + + runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using ExampleApi.Models; + +namespace ExampleApi.Controllers +{ + public sealed partial class ItemsController : JsonApiCommandController + { + public ItemsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceCommandService commandService) + : base(options, resourceGraph, loggerFactory, commandService) + { + } + } +} +"); + } + + [Fact] + public void Can_generate_for_mixed_controller() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .WithNamespaceImportFor(typeof(JsonApiEndpoints)) + .InNamespace("ExampleApi.Models") + .WithCode(@" + [Resource(GenerateControllerEndpoints = NoRelationshipEndpoints)] + public sealed class Item : Identifiable + { + private const JsonApiEndpoints NoRelationshipEndpoints = JsonApiEndpoints.GetCollection | + JsonApiEndpoints.GetSingle | JsonApiEndpoints.Post | JsonApiEndpoints.Patch | JsonApiEndpoints.Delete; + + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + + runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using ExampleApi.Models; + +namespace ExampleApi.Controllers +{ + public sealed partial class ItemsController : JsonApiController + { + public ItemsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IGetAllService getAll, + IGetByIdService getById, + ICreateService create, + IUpdateService update, + IDeleteService delete) + : base(options, resourceGraph, loggerFactory, + getAll: getAll, + getById: getById, + create: create, + update: update, + delete: delete) + { + } + } +} +"); + } + + [Fact] + public void Skips_for_resource_without_ResourceAttribute() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(AttrAttribute)) + .InNamespace("ExampleApi.Models") + .WithCode(@" + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + runResult.Should().NotHaveProducedSourceCode(); + } + + [Fact] + public void Skips_for_resource_with_no_endpoints() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .WithNamespaceImportFor(typeof(JsonApiEndpoints)) + .InNamespace("ExampleApi.Models") + .WithCode(@" + [Resource(GenerateControllerEndpoints = JsonApiEndpoints.None)] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + runResult.Should().NotHaveProducedSourceCode(); + } + + [Fact] + public void Skips_for_missing_dependency_on_JsonApiDotNetCore() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .InNamespace("ExampleApi.Models") + .WithCode(@" + public abstract class Identifiable + { + } + + public sealed class ResourceAttribute : System.Attribute + { + } + + public sealed class AttrAttribute : System.Attribute + { + } + + [Resource] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithSystemReferences() + .WithLoggerFactoryReference() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + + runResult.Should().NotHaveProducedSourceCode(); + } + + [Fact] + public void Skips_for_missing_dependency_on_LoggerFactory() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .InNamespace("ExampleApi.Models") + .WithCode(@" + [Resource] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithSystemReferences() + .WithJsonApiDotNetCoreReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + + runResult.Should().NotHaveProducedSourceCode(); + } + + [Fact] + public void Warns_for_resource_that_does_not_implement_IIdentifiable() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .InNamespace("ExampleApi.Models") + .WithCode(@" + [Resource] + public sealed class Item + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + + runResult.Should() + .HaveSingleDiagnostic( + "(6,21): warning JADNC001: Type 'Item' must implement IIdentifiable when using ResourceAttribute to auto-generate ASP.NET controllers"); + + runResult.Should().NotHaveProducedSourceCode(); + } + + [Fact] + public void Adds_nullable_enable_for_nullable_reference_ID_type() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .InNamespace("ExampleApi.Models") + .WithCode(@" + #nullable enable + + [Resource] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + + runResult.Should().HaveProducedSourceCodeContaining(@"#nullable enable"); + } + + [Fact] + public void Can_generate_for_custom_namespace() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .InNamespace("ExampleApi.Models") + .WithCode(@" + [Resource(ControllerNamespace = ""Some.Path.To.Generate.Code.In"")] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + + runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using ExampleApi.Models; + +namespace Some.Path.To.Generate.Code.In +{ + public sealed partial class ItemsController : JsonApiController + { + public ItemsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, resourceGraph, loggerFactory, resourceService) + { + } + } +} +"); + } + + [Fact] + public void Can_generate_for_top_level_namespace() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .InNamespace("TopLevel") + .WithCode(@" + [Resource] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + + runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using TopLevel; + +namespace Controllers +{ + public sealed partial class ItemsController : JsonApiController + { + public ItemsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, resourceGraph, loggerFactory, resourceService) + { + } + } +} +"); + } + + [Fact] + public void Can_generate_for_global_namespace() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .WithCode(@" + [Resource] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + + runResult.Should().HaveProducedSourceCode(@"using Microsoft.Extensions.Logging; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; + +public sealed partial class ItemsController : JsonApiController +{ + public ItemsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, + IResourceService resourceService) + : base(options, resourceGraph, loggerFactory, resourceService) + { + } +} +"); + } + + [Fact] + public void Generates_unique_file_names_for_duplicate_resource_name_in_different_namespaces() + { + // Arrange + GeneratorDriver driver = CSharpGeneratorDriver.Create(new ControllerSourceGenerator()); + + // @formatter:wrap_chained_method_calls chop_always + // @formatter:keep_existing_linebreaks true + + string source = new SourceCodeBuilder() + .WithNamespaceImportFor(typeof(IIdentifiable)) + .WithNamespaceImportFor(typeof(ResourceAttribute)) + .WithCode(@" + namespace The.First.One + { + [Resource] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + } + } + + namespace The.Second.One + { + [Resource] + public sealed class Item : Identifiable + { + [Attr] + public int Value { get; set; } + } + }") + .Build(); + + Compilation inputCompilation = new CompilationBuilder() + .WithDefaultReferences() + .WithSourceCode(source) + .Build(); + + // @formatter:keep_existing_linebreaks restore + // @formatter:wrap_chained_method_calls restore + + // Act + driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out Compilation outputCompilation, out _); + + // Assert + inputCompilation.GetDiagnostics().Should().BeEmpty(); + outputCompilation.GetDiagnostics().Should().BeEmpty(); + + GeneratorDriverRunResult runResult = driver.GetRunResult(); + runResult.Should().NotHaveDiagnostics(); + runResult.Results.Should().HaveCount(1); + + GeneratorRunResult generatorResult = runResult.Results[0]; + generatorResult.GeneratedSources.Should().HaveCount(2); + + generatorResult.GeneratedSources[0].HintName.Should().Be("ItemsController.g.cs"); + generatorResult.GeneratedSources[1].HintName.Should().Be("ItemsController2.g.cs"); + } + } +} diff --git a/test/SourceGeneratorTests/GeneratorDriverRunResultExtensions.cs b/test/SourceGeneratorTests/GeneratorDriverRunResultExtensions.cs new file mode 100644 index 0000000000..ab7452c28a --- /dev/null +++ b/test/SourceGeneratorTests/GeneratorDriverRunResultExtensions.cs @@ -0,0 +1,77 @@ +using FluentAssertions; +using FluentAssertions.Primitives; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace SourceGeneratorTests +{ + internal static class GeneratorDriverRunResultExtensions + { + public static GeneratorDriverRunResultAssertions Should(this GeneratorDriverRunResult instance) + { + return new GeneratorDriverRunResultAssertions(instance); + } + + internal sealed class GeneratorDriverRunResultAssertions : ReferenceTypeAssertions + { + protected override string Identifier => nameof(GeneratorDriverRunResult); + + public GeneratorDriverRunResultAssertions(GeneratorDriverRunResult subject) + : base(subject) + { + } + + public void NotHaveDiagnostics() + { + Subject.Diagnostics.Should().BeEmpty(); + } + + public void HaveSingleDiagnostic(string message) + { + Subject.Diagnostics.Should().HaveCount(1); + Subject.Diagnostics[0].ToString().Should().Be(message); + } + + public void NotHaveProducedSourceCode() + { + Subject.Results.Should().HaveCount(1); + + GeneratorRunResult generatorResult = Subject.Results[0]; + generatorResult.GeneratedSources.Should().BeEmpty(); + } + + public void HaveProducedSourceCode(string expectedCode) + { + string generatedSourceText = GetGeneratedSourceText(); + + string? generatedSourceTextNormalized = NormalizeLineEndings(generatedSourceText); + string? expectedTextNormalized = NormalizeLineEndings(expectedCode); + + generatedSourceTextNormalized.Should().Be(expectedTextNormalized); + } + + public void HaveProducedSourceCodeContaining(string expectedText) + { + string generatedSourceText = GetGeneratedSourceText(); + + generatedSourceText.Should().Contain(expectedText); + } + + private string GetGeneratedSourceText() + { + Subject.Results.Should().HaveCount(1); + + GeneratorRunResult generatorResult = Subject.Results[0]; + generatorResult.GeneratedSources.Should().HaveCount(1); + + SourceText generatedSource = generatorResult.GeneratedSources[0].SourceText; + return generatedSource.ToString(); + } + + private static string? NormalizeLineEndings(string? text) + { + return text?.Replace("\r\n", "\n").Replace("\r", "\n"); + } + } + } +} diff --git a/test/SourceGeneratorTests/JsonApiEndpointsCopyTests.cs b/test/SourceGeneratorTests/JsonApiEndpointsCopyTests.cs new file mode 100644 index 0000000000..cb8fa21c57 --- /dev/null +++ b/test/SourceGeneratorTests/JsonApiEndpointsCopyTests.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using FluentAssertions; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.SourceGenerators; +using Xunit; + +namespace SourceGeneratorTests +{ + public sealed class JsonApiEndpointsCopyTests + { + [Fact] + public void Enum_underlying_types_are_identical() + { + Type sourceType = Enum.GetUnderlyingType(typeof(JsonApiEndpoints)); + Type copyType = Enum.GetUnderlyingType(typeof(JsonApiEndpointsCopy)); + + copyType.Should().Be(sourceType); + } + + [Fact] + public void Enum_attributes_in_order_are_identical() + { + Attribute[] sourceAttributes = typeof(JsonApiEndpoints).GetCustomAttributes().ToArray(); + Attribute[] copyAttributes = typeof(JsonApiEndpointsCopy).GetCustomAttributes().ToArray(); + + copyAttributes.Should().BeEquivalentTo(sourceAttributes, options => options.WithStrictOrdering()); + } + + [Fact] + public void Enum_member_names_in_order_are_identical() + { + const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly; + + string[] sourceNames = typeof(JsonApiEndpoints).GetMembers(bindingFlags).Select(memberInfo => memberInfo.Name).ToArray(); + string[] copyNames = typeof(JsonApiEndpointsCopy).GetMembers(bindingFlags).Select(memberInfo => memberInfo.Name).ToArray(); + + copyNames.Should().BeEquivalentTo(sourceNames, options => options.WithStrictOrdering()); + } + + [Fact] + public void Enum_member_values_are_identical() + { + IEnumerable sourceValues = Enum.GetValues().Select(value => (int)value).ToArray(); + int[] copyValues = Enum.GetValues().Select(value => (int)value).ToArray(); + + copyValues.Should().BeEquivalentTo(sourceValues, options => options.WithStrictOrdering()); + } + } +} diff --git a/test/SourceGeneratorTests/SourceCodeBuilder.cs b/test/SourceGeneratorTests/SourceCodeBuilder.cs new file mode 100644 index 0000000000..fbf436e6dd --- /dev/null +++ b/test/SourceGeneratorTests/SourceCodeBuilder.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SourceGeneratorTests +{ + internal sealed class SourceCodeBuilder + { + private readonly HashSet _namespaceImports = new(); + private string? _namespace; + private string? _code; + + public string Build() + { + StringBuilder builder = new(); + + if (_namespaceImports.Any()) + { + foreach (var namespaceImport in _namespaceImports) + { + builder.AppendLine($"using {namespaceImport};"); + } + + builder.AppendLine(); + } + + if (_namespace != null) + { + builder.AppendLine($"namespace {_namespace}"); + builder.AppendLine("{"); + } + + if (_code != null) + { + builder.Append(_code); + } + + if (_namespace != null) + { + builder.AppendLine("}"); + } + + return builder.ToString(); + } + + public SourceCodeBuilder WithNamespaceImportFor(Type type) + { + _namespaceImports.Add(type.Namespace!); + return this; + } + + public SourceCodeBuilder InNamespace(string @namespace) + { + _namespace = @namespace; + return this; + } + + public SourceCodeBuilder WithCode(string code) + { + _code = code; + return this; + } + } +} diff --git a/test/SourceGeneratorTests/SourceGeneratorTests.csproj b/test/SourceGeneratorTests/SourceGeneratorTests.csproj new file mode 100644 index 0000000000..7b073ae6b8 --- /dev/null +++ b/test/SourceGeneratorTests/SourceGeneratorTests.csproj @@ -0,0 +1,18 @@ + + + $(NetCoreAppVersion) + + + + + + + + + + + + + + + diff --git a/test/TestBuildingBlocks/FakerContainer.cs b/test/TestBuildingBlocks/FakerContainer.cs index 785eb510eb..4ad4aa873f 100644 --- a/test/TestBuildingBlocks/FakerContainer.cs +++ b/test/TestBuildingBlocks/FakerContainer.cs @@ -48,7 +48,7 @@ private static bool IsTestMethod(MethodBase? method) return false; } - return method.GetCustomAttribute(typeof(FactAttribute)) != null || method.GetCustomAttribute(typeof(TheoryAttribute)) != null; + return method.GetCustomAttribute() != null || method.GetCustomAttribute() != null; } private static int GetDeterministicHashCode(string source)