RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.7k stars 1.24k forks source link

Potential bug in TypeScript classes (DTOs) init method with Kockoutjs #3191

Closed JohanAJ closed 3 years ago

JohanAJ commented 3 years ago

I'm working on a project that is using Knockoutjs. Generating the TypeScript client (using NSwagStudio) the generated types all have the following init(data?: any) {}, inside this method, however, the property _data (prefixed underscore) is used instead of just data.

Example of generated code: (default settings used in NSwagStudio, with the template being Fetch and the DTO type style as KnockoutClass.

export class PagedResultBase {
    currentPage = ko.observable<number>();
    pageCount = ko.observable<number>();
    pageSize = ko.observable<number>();
    rowCount = ko.observable<number>();
    firstRowOnPage = ko.observable<number>();
    lastRowOnPage = ko.observable<number>();

    init(data?: any) {
        if (data !== undefined) {
            var currentPage_: any;
            currentPage_ = _data["currentPage"];
            this.currentPage(currentPage_);

            var pageCount_: any;
            pageCount_ = _data["pageCount"];
            this.pageCount(pageCount_);

            var pageSize_: any;
            pageSize_ = _data["pageSize"];
            this.pageSize(pageSize_);

            var rowCount_: any;
            rowCount_ = _data["rowCount"];
            this.rowCount(rowCount_);

            var firstRowOnPage_: any;
            firstRowOnPage_ = _data["firstRowOnPage"];
            this.firstRowOnPage(firstRowOnPage_);

            var lastRowOnPage_: any;
            lastRowOnPage_ = _data["lastRowOnPage"];
            this.lastRowOnPage(lastRowOnPage_);

        }
    }
}
MrLunde commented 3 years ago

I also ran into the same problem today.

JohanAJ commented 3 years ago

This is an issue on the NJsonSchema solution, PR created to fix this.

EJocys commented 1 year ago

I still have the same problem. Test project consisting of two simple files:

NSwagTest.csproj file:

<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
    </PropertyGroup>

    <ItemGroup>
      <TypeScriptCompile Remove="ApiClient.ts" />
    </ItemGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.TypeScript.MSBuild" Version="4.8.4" />
        <PackageReference Include="NSwag.AspNetCore" Version="13.17.0" />
        <PackageReference Include="NSwag.MSBuild" Version="13.17.0" />
    </ItemGroup>

    <Target Name="NSwagGenerateApiClientAngularSourceCode" AfterTargets="PostBuildEvent">
        <!-- Generate swagger.json specification file from API. -->
        <!-- Get help about arguments: dotnet "%USERPROFILE%\.nuget\packages\nswag.msbuild\13.16.1\tools\Net60\dotnet-nswag.dll" help aspnetcore2openapi -->
        <Exec Command="$(NSwagExe_Net60) aspnetcore2openapi /AspNetCoreEnvironment:Development /project:&quot;$(ProjectPath)&quot; /output:&quot;$(TargetPath).swagger.json&quot; /nobuild:true" />
        <!-- Generate TypeScript API Client from swagger.json specification. -->
        <!-- Get help about arguments: dotnet "%USERPROFILE%\.nuget\packages\nswag.msbuild\13.16.1\tools\Net60\dotnet-nswag.dll" help openapi2tsclient -->
        <Exec Command="$(NSwagExe_Net60) openapi2tsclient /AspNetCoreEnvironment:Development /input:&quot;$(TargetPath).swagger.json&quot; /output:&quot;ApiClient.ts&quot; /TypeScriptVersion:4.3 /Template:JQueryPromises  /TypeStyle:KnockoutClass /InjectionTokenType:InjectionToken /UseSingletonProvider:true /UseBaseUrl:false /GenerateClientInterfaces:false" />
    </Target>

</Project>

Main.cs file:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApiDocument();
var app = builder.Build();

app.MapGet("/GetUser", User () => new User() { Id = 1, Name = "User Name" });
app.Run();

public class User {
    public int Id { get; set; }
    public string Name { get; set; } = "";
}

Building the project auto-generate ApiClient.ts file where the property _data (prefixed underscore) is used instead of just data:

//----------------------
// <auto-generated>
//     Generated using the NSwag toolchain v13.17.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)
// </auto-generated>

...
export class User {
    id = ko.observable<number>();
    name = ko.observable<string>();

    init(data?: any) {
        if (data !== undefined) {
            var id_: any;
                id_ = _data["id"];
            this.id(id_);

            var name_: any;
                name_ = _data["name"];
            this.name(name_);

        }
    }
...

Therefore ApiClient.ts won't pass TypeScript validation: image

One of the simple workarounds would be to replace _data[ with data[ by adding command element to the bottom of the element:

<!-- Workaround: Fix KnockoutClass file -->
<Exec Command="powershell.exe –command &quot;&amp; { (Get-Content 'ApiClient.ts').replace('_data[', 'data[') | Set-Content 'ApiClient.ts' }&quot;" />