diff --git a/CHANGELOG.md b/CHANGELOG.md index e75f8a6..fbded91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 17ff748..4227fb4 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/TypeContractor.Annotations/TypeContractorIgnoreAttribute.cs b/TypeContractor.Annotations/TypeContractorIgnoreAttribute.cs index f520890..6762c60 100644 --- a/TypeContractor.Annotations/TypeContractorIgnoreAttribute.cs +++ b/TypeContractor.Annotations/TypeContractorIgnoreAttribute.cs @@ -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. + /// + /// When generating API clients, a single endpoint can be ignored using + /// this attribute. + /// /// - [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 { } diff --git a/TypeContractor.Tests/Helpers/ApiHelpersTests.cs b/TypeContractor.Tests/Helpers/ApiHelpersTests.cs index 3fc4afc..b0cae8f 100644 --- a/TypeContractor.Tests/Helpers/ApiHelpersTests.cs +++ b/TypeContractor.Tests/Helpers/ApiHelpersTests.cs @@ -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 { } @@ -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")] diff --git a/TypeContractor/Helpers/ApiHelpers.cs b/TypeContractor/Helpers/ApiHelpers.cs index 8736367..0b95e04 100644 --- a/TypeContractor/Helpers/ApiHelpers.cs +++ b/TypeContractor/Helpers/ApiHelpers.cs @@ -17,8 +17,8 @@ public static partial class ApiHelpers public static ApiClient? BuildApiClient(Type controller, List 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; @@ -50,6 +50,13 @@ public static partial class ApiHelpers internal static List 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