Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- Allow `TypeContractorIgnore` to be used on controller methods

## [0.20.0] - 2026-05-30

### Added
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ Available annotations:
generated, you can annotate that controller using `TypeContractorIgnore`
and it will be automatically skipped.

Also works on properties that shouldn't be included in generated DTOs.
Also works on properties that shouldn't be included in generated DTOs,
or on endpoints in a controller if you only want some of the endpoints.
* `TypeContractorName`:
If you have a badly named controller that you can't rename,
you want something custom, or just don't like the default naming
Expand Down
6 changes: 5 additions & 1 deletion TypeContractor.Annotations/TypeContractorIgnoreAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ namespace TypeContractor.Annotations
/// Tells TypeContractor to ignore the target when generating TypeScript.
/// For example a controller that serves static assets for HTML templates.
/// Can also be used for properties that don't need to be exposed.
/// <para>
/// When generating API clients, a single endpoint can be ignored using
/// this attribute.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class TypeContractorIgnoreAttribute : Attribute
{
}
Expand Down
17 changes: 17 additions & 0 deletions TypeContractor.Tests/Helpers/ApiHelpersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ public void BuildApiEndpoint_Generates_Name()
endpoint.First().Name.Should().Be("overloadEndpoint");
}

[Fact]
public void BuildApiEndpoint_Skips_Ignored_Methods()
{
// Arrange
var endpointMethod = typeof(LegacyController).GetMethod(nameof(LegacyController.NothingToSeeHere), [typeof(CancellationToken)])!;

// Act
var endpoint = ApiHelpers.BuildApiEndpoint(endpointMethod);

// Assert
endpoint.Should().BeEmpty();
}

[TypeContractorIgnore]
internal class IgnoredController : ControllerBase { }

Expand All @@ -72,6 +85,10 @@ internal class LegacyController : ControllerBase

[HttpGet("other-route")]
public ActionResult OverloadEndpoint(CancellationToken cancellationToken) => NotFound();

[HttpPatch("third-route")]
[TypeContractorIgnore]
public ActionResult NothingToSeeHere(CancellationToken cancellationToken) => NotFound();
}

[TypeContractorName("RenamedApi")]
Expand Down
11 changes: 9 additions & 2 deletions TypeContractor/Helpers/ApiHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public static partial class ApiHelpers

public static ApiClient? BuildApiClient(Type controller, List<MethodInfo> endpoints)
{
var ignoreAttribute = controller.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == typeof(TypeContractorIgnoreAttribute).FullName);
if (ignoreAttribute is not null)
var hasIgnoreAttribute = controller.HasCustomAttribute(typeof(TypeContractorIgnoreAttribute).FullName ?? "");
if (hasIgnoreAttribute)
{
Log.Instance.LogDebug($"Controller {controller.Name} marked with Ignore. Skipping.");
return null;
Expand Down Expand Up @@ -50,6 +50,13 @@ public static partial class ApiHelpers

internal static List<ApiClientEndpoint> BuildApiEndpoint(MethodInfo endpoint)
{
// See if we should skip
if (endpoint.HasCustomAttribute(typeof(TypeContractorIgnoreAttribute).FullName ?? ""))
{
Log.Instance.LogDebug($"Method {endpoint.Name} marked with Ignore. Skipping.");
return [];
}

// Find HTTP method
var httpAttributes = endpoint
.CustomAttributes
Expand Down
Loading