Skip to content

Auto-generated JSON:API controllers using source generators #1117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 3, 2021

Conversation

bart-degreed
Copy link
Contributor

@bart-degreed bart-degreed commented Nov 25, 2021

Usage

To generate a controller, decorate your resource class with [Resource]. For example:

[Resource] // Generates ProductsController.g.cs
public class Product : Identifiable<long>
{
    // ...
}

This enables to choose per resource type if a controller should be generated or not.

A subset of exposed endpoints can be specified too:

[Resource(GenerateControllerEndpoints = JsonApiEndpoints.GetCollection | JsonApiEndpoints.GetSingle)]
public class Product : Identifiable<long>
{
    // ...
}

Auto-generated controllers can easily be augmented because they are partial classes. For example:

[DisableRoutingConvention]
[Route("some/custom/route")]
[DisableQueryString(JsonApiQueryStringParameters.Include)]
partial class ProductsController
{
    [HttpPost]
    public IActionResult Upload()
    {
        // ...
    }
}

In Visual Studio, Solution Explorer shows what code was generated:

image

Implementation

Alternatives

An alternative solution I considered is to dynamically register controllers to ASP.NET at runtime, basically #900.

Several reasons why I believe source generators are the better choice here:

  • Debugging: developers can inspect what was generated, breakpoints can be added to step into auto-generated controllers.
  • Flexibility in augmenting the generated controllers with attributes and custom code, they are partial classes.
  • Reduced startup costs, no reflection, or facing cryptic runtime resolve errors.
  • Provide feedback at compile time by emitting build warnings, for example: "resource does not implement IIdentifiable".
  • Less code to implement in JADNC: many checks are caught by the compiler, such as missing references/usings, duplicate classes.
  • Easy to unit test: does not require starting up a web server.

Notes:

  • Fixed: Attributes DisableRoutingConvention, DisableQueryString, JsonPropertyName and Resource used on base classes did not work.
  • Reduced dependency from Humanizer to Humanizer.Core, which only contains basic rules and the English language. I assumed that calls like Pluralize() would take language rules into account, but this is not the case. The localized satellite assemblies just contain texts such as "one week ago", "yesterday" etc, which we don't use.
  • ResourceAttribute.PublicName is now optional. This means existing code [Resource("articles")] needs to be changed to: [Resource(PublicName: "articles")].
  • Use [ActivatorUtilitiesConstructor] to force the IoC container to choose your own controller constructor with extra dependencies.
  • Visual Studio is unable to unload/reload source generators. So if editing the source generator code and things don't work as expected, restart the IDE.
  • Rider is unable to debug into source generators, except when debugging from a source-generator unit test.

Helpful links for future reference (when developing source generators):

Closes #732
Closes #365

QUALITY CHECKLIST

  • Changes implemented in code
  • Complies with our contributing guidelines
  • Adapted tests
  • Documentation updated
  • N/A: Created issue to update Templates: {ISSUE_NUMBER}

@bart-degreed bart-degreed force-pushed the auto-generated-controllers branch 4 times, most recently from 38f50e6 to 0613b3e Compare November 27, 2021 03:14
@codecov
Copy link

codecov bot commented Nov 27, 2021

Codecov Report

Merging #1117 (b545d90) into master (e65b65a) will increase coverage by 0.40%.
The diff coverage is 97.59%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1117      +/-   ##
==========================================
+ Coverage   89.00%   89.41%   +0.40%     
==========================================
  Files         249      245       -4     
  Lines        7123     7294     +171     
==========================================
+ Hits         6340     6522     +182     
+ Misses        783      772      -11     
Impacted Files Coverage Δ
...Examples/JsonApiDotNetCoreExample/Models/Person.cs 0.00% <ø> (ø)
...rc/Examples/JsonApiDotNetCoreExample/Models/Tag.cs 0.00% <ø> (ø)
...amples/JsonApiDotNetCoreExample/Models/TodoItem.cs 0.00% <ø> (ø)
...Examples/MultiDbContextExample/Models/ResourceA.cs 100.00% <ø> (ø)
...Examples/MultiDbContextExample/Models/ResourceB.cs 100.00% <ø> (ø)
...amples/NoEntityFrameworkExample/Models/WorkItem.cs 100.00% <ø> (ø)
...rollers/Annotations/DisableQueryStringAttribute.cs 100.00% <ø> (ø)
...re/Resources/Annotations/ResourceLinksAttribute.cs 100.00% <ø> (ø)
...iDotNetCore/Configuration/ResourceNameFormatter.cs 88.88% <66.66%> (+1.38%) ⬆️
...ApiDotNetCore.SourceGenerators/SourceCodeWriter.cs 97.58% <97.58%> (ø)
... and 9 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e65b65a...b545d90. Read the comment docs.

@bart-degreed bart-degreed force-pushed the auto-generated-controllers branch 2 times, most recently from c25c019 to ac84993 Compare November 27, 2021 04:24
@bart-degreed bart-degreed marked this pull request as ready for review November 27, 2021 16:26
@bart-degreed bart-degreed requested a review from maurei November 27, 2021 16:27
@bart-degreed bart-degreed force-pushed the auto-generated-controllers branch from 966ae83 to e68b2a5 Compare November 27, 2021 17:23
@bart-degreed bart-degreed force-pushed the auto-generated-controllers branch from 0042b94 to b545d90 Compare December 2, 2021 23:56
bart-degreed pushed a commit that referenced this pull request Dec 3, 2021
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

Empty commit to republish docs from master branch
bart-degreed pushed a commit that referenced this pull request Dec 3, 2021
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
bart-degreed pushed a commit that referenced this pull request Dec 3, 2021
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
bart-degreed pushed a commit that referenced this pull request Dec 3, 2021
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
@maurei maurei merged commit 9c9a4f2 into master Dec 3, 2021
@maurei maurei deleted the auto-generated-controllers branch December 3, 2021 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants