From 1b65f053de8f224a2cbad7734ccca4bef7b5c8a5 Mon Sep 17 00:00:00 2001 From: "Per Christian B. Viken" Date: Sun, 16 Feb 2025 10:18:35 +0100 Subject: [PATCH] fix(lib): Handle enums as query parameters #133 --- CHANGELOG.md | 4 ++ .../TypeScript/ApiClientWriterTests.cs | 59 +++++++++++++++++++ TypeContractor/Helpers/CasingHelpers.cs | 2 +- TypeContractor/TypeScript/ApiClientWriter.cs | 6 ++ 4 files changed, 70 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58661b7..5c14dc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Better support for generic types (#123) +### Fixed + +- Allow enums to be used as query parameters in API clients (#133) + ## [0.16.0] - 2024-12-17 ### Added diff --git a/TypeContractor.Tests/TypeScript/ApiClientWriterTests.cs b/TypeContractor.Tests/TypeScript/ApiClientWriterTests.cs index 9d9e43d..b59383f 100644 --- a/TypeContractor.Tests/TypeScript/ApiClientWriterTests.cs +++ b/TypeContractor.Tests/TypeScript/ApiClientWriterTests.cs @@ -185,6 +185,59 @@ public void Writes_Get_With_Route() .And.NotContain("url.searchParams.append("); } + [Fact] + public void Handles_Get_With_Route_And_Query_Parameters() + { + // Arrange + var apiClient = new ApiClient("TestClient", "TestController", "test", null); + apiClient.AddEndpoint(new ApiClientEndpoint("getLatestId", "latest/{year}", EndpointMethod.GET, null, typeof(Guid), false, + [new EndpointParameter("year", typeof(int), null, false, false, true, false, false, false, false, false), + new EndpointParameter("reverse", typeof(bool?), null, false, false, false, true, false, false, false, true)], null)); + + // Act + var result = Sut.Write(apiClient, [], _converter, true, _templateFn, Casing.Pascal); + + // Assert + var file = File.ReadAllText(result).Trim(); + file.Should() + .NotBeEmpty() + .And.Contain("import { z } from 'zod';") + .And.Contain("export class TestClient {") + .And.Contain("public async getLatestId(year: number, reverse: boolean | undefined, cancellationToken: AbortSignal = null): Promise {") + .And.Contain("const url = new URL(`test/latest/${year}`, window.location.origin);") + .And.Contain("if (!!reverse)") + .And.Contain("url.searchParams.append('reverse', reverse.toString());"); + } + + [Fact] + public void Handles_Enum_As_Query_Parameter() + { + // Arrange + var outputTypes = new List + { + _converter.Convert(typeof(ReportType)) + }; + + var apiClient = new ApiClient("TestClient", "TestController", "test", null); + apiClient.AddEndpoint(new ApiClientEndpoint("getLatestId", "latest/{year}", EndpointMethod.GET, null, typeof(Guid), false, + [new EndpointParameter("year", typeof(int), null, false, false, true, false, false, false, false, false), + new EndpointParameter("reportType", typeof(ReportType), null, false, false, false, true, false, false, false, false)], null)); + + // Act + var result = Sut.Write(apiClient, outputTypes, _converter, true, _templateFn, Casing.Pascal); + + // Assert + var file = File.ReadAllText(result).Trim(); + file.Should() + .NotBeEmpty() + .And.Contain("import { z } from 'zod';") + .And.Contain("export class TestClient {") + .And.Contain("public async getLatestId(year: number, reportType: ReportType, cancellationToken: AbortSignal = null): Promise {") + .And.Contain("const url = new URL(`test/latest/${year}`, window.location.origin);") + .And.NotContain("if (!!reportType)") + .And.Contain("url.searchParams.append('reportType', reportType.toString());"); + } + [Fact] public void Unpacks_Complex_Object_To_Query() { @@ -290,6 +343,12 @@ private class PaginatedRequest public int PageSize { get; set; } } + private enum ReportType + { + Summary = 0, + Detailed = 1, + } + private MetadataLoadContext BuildMetadataLoadContext() { // Get the array of runtime assemblies. diff --git a/TypeContractor/Helpers/CasingHelpers.cs b/TypeContractor/Helpers/CasingHelpers.cs index 64d5361..bf20980 100644 --- a/TypeContractor/Helpers/CasingHelpers.cs +++ b/TypeContractor/Helpers/CasingHelpers.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using System.Text; using System.Text.RegularExpressions; diff --git a/TypeContractor/TypeScript/ApiClientWriter.cs b/TypeContractor/TypeScript/ApiClientWriter.cs index 40062d7..7b0a9e4 100644 --- a/TypeContractor/TypeScript/ApiClientWriter.cs +++ b/TypeContractor/TypeScript/ApiClientWriter.cs @@ -91,6 +91,12 @@ public string Write(ApiClient apiClient, IEnumerable allTypes, TypeS continue; } + if (outputType.IsEnum) + { + queryParamsDto.Add(new QueryParameterTemplateDto(queryParam.Name, true, false, false, queryParam.IsOptional, null)); + continue; + } + foreach (var property in outputType.Properties ?? []) { queryParamsDto.Add(new QueryParameterTemplateDto(queryParam.Name, false, property.IsNullable, false, queryParam.IsOptional, property.DestinationName));