Skip to content
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

Typescript Client Generation problem with objects being unrolled #4791

Open
awdorrin opened this issue Feb 22, 2024 · 6 comments
Open

Typescript Client Generation problem with objects being unrolled #4791

awdorrin opened this issue Feb 22, 2024 · 6 comments

Comments

@awdorrin
Copy link

Starting with a C# Controller with a method like this:

[HttpPut("[action]")]
public async Task<IActionResult> PutSubcRtgLink(int subcontractRtgLinkId, SubcRtgLinkDTO subcRtgLink)

Compiling for .Net 6, using NSwag.AspnetCore/NSwag.MSBuild 13.18.2, would generate this typescript:

putSubcRtgLink(subcontractRtgLinkId: number, subcRtgLink: SubcRtgLinkDTO): Observable<FileResponse> {

After upgrading to .Net8 and NSwag.AspnetCore/NSwag.MSBuild 14.0.3, the SubcRtgLinkDTO is being un-rolled into multiple parameters, which ends up repeating the subcontractRtgLinkId parameter, like this:

putSubcRtgLink(subcontractRtgLinkIdQuery: number | undefined, subcontractRtgLinkIdQuery: number | undefined, subcontractId: number | undefined, pmmProgramId: number | undefined, subcontractRtgId: number | undefined, fileName: string | null | undefined, fileDescription: string | null | undefined, linkAddress: string | null | undefined, sipLabel: string | null | undefined, dateAdded: DateTime | undefined, dateModified: DateTime | undefined, modifiedBy: string | null | undefined, file: FileParameter | null | undefined): Observable<FileResponse | null> {

Not sure if this is a bug, or if something changed and I need to add some new parameters to the nswag.json file.
Searched but didn't see anything related...

@awdorrin
Copy link
Author

Making some progress, as I'm reading through the thread here: #4524

Looks like my nswag.json was not being read, which was a large part of the problem.
Fixed by updating my csproj file:

<Target Name="NSwag" AfterTargets="Build">
  <Exec Condition="'$(Configuration)' == 'Debug'" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Development" Command="$(NSwagExe_Net80) run nswag.json /variables:Configuration=$(Configuration)" />
  <Exec Condition="'$(Configuration)' == 'Release'" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Production" Command="$(NSwagExe_Net80) run nswag.json /variables:Configuration=$(Configuration)" />
</Target>`

Now the vast majority of my method signatures are the same, between NSwag toolchain v13.18.2.0 and v14.0.3.0, discounting all the new | null or | undefined additions.

I now only have one methods, out of several hundred, that is being unrolled... the same one I mentioned in my initial post.

Original: putSubcRtgLink(subcontractRtgLinkId: number, subcRtgLink: SubcRtgLinkDTO): Observable<FileResponse> {
Now: putSubcRtgLink(subcontractRtgLinkIdQuery?: number | undefined, subcontractRtgLinkIdQuery?: number | undefined, subcontractId?: number | undefined, pmmProgramId?: number | undefined, subcontractRtgId?: number | undefined, fileName?: string | null | undefined, fileDescription?: string | null | undefined, linkAddress?: string | null | undefined, sipLabel?: string | null | undefined, dateAdded?: DateTime | undefined, dateModified?: DateTime | undefined, modifiedBy?: string | null | undefined, file?: FileParameter | null | undefined): Observable<FileResponse | null> {

Code in the controller:

        [HttpPut("[action]")]
        public async Task<IActionResult> PutSubcRtgLink(int subcontractRtgLinkId, SubcRtgLinkDTO subcRtgLink)

Definition of SubcRtgLinkDTO:

    public partial class SubcRtgLinkDTO
    {
        public int SubcontractRtgLinkId { get; set; }
        public int SubcontractId { get; set; }
        public int PmmProgramId { get; set; }
        public int SubcontractRtgId { get; set; }
        public string FileName { get; set; }
        public string FileDescription { get; set; }
        public string LinkAddress { get; set; }
        public string SipLabel { get; set; }
        public DateTime DateAdded { get; set; }
        public DateTime DateModified { get; set; }
        public string ModifiedBy { get; set; }
        public IFormFile File { get; set; }
        public SubcRtgLinkDTO() { }

I seem to recall running into a similar issue with IFormFile and NSwag in another application, so I'm guessing that may be what is unrolling the SubcRtgLinkDTO object into its parameters...

Comparing the definition of SubcRtgLinkDTO, it does indicate it is an issue related to IFormFile.
Older NSwag gave:

export class SubcRtgLinkDTO implements ISubcRtgLinkDTO {
    subcontractRtgLinkId?: number;
    subcontractId?: number;
    pmmProgramId?: number;
    subcontractRtgId?: number;
    fileName?: string | undefined;
    fileDescription?: string | undefined;
    linkAddress?: string | undefined;
    sipLabel?: string | undefined;
    dateAdded?: DateTime;
    dateModified?: DateTime;
    modifiedBy?: string | undefined;
    file?: string | undefined;

while the new NSwag gives:

export class SubcRtgLinkDTO implements ISubcRtgLinkDTO {
    subcontractRtgLinkId!: number;
    subcontractId!: number;
    pmmProgramId!: number;
    subcontractRtgId!: number;
    fileName?: string | undefined;
    fileDescription?: string | undefined;
    linkAddress?: string | undefined;
    sipLabel?: string | undefined;
    dateAdded!: DateTime;
    dateModified!: DateTime;
    modifiedBy?: string | undefined;
    file?: any | undefined;

@awdorrin
Copy link
Author

Spoke too soon, I found about a dozen method calls where the order of parameters changed. For example:
C#:
public async Task<IActionResult> PutMarketStreamEnterpriseLead(int id, MarketStreamEnterpriseLead msel)

Typescript:
putMarketStreamEnterpriseLead(msel: MarketStreamEnterpriseLead, id?: number | undefined): Observable<FileResponse | null>

I'm not sure why it is seeing the id parameter as optional/nullable.

@awdorrin
Copy link
Author

Work around for the parameter order issue:
public async Task<IActionResult> PutMarketStreamEnterpriseLead( [BindRequired] int id, MarketStreamEnterpriseLead msel)
I had to add [BindRequired] to 5 put methods in our app, out of 75 total put methods.
Not sure why the other 70 work without issue?

@lecramr
Copy link

lecramr commented Apr 8, 2024

@awdorrin
Every fixed this Issue? I have the same one, as soon as I add a IFormFile to the DTO it gets unrolled.

@awdorrin
Copy link
Author

awdorrin commented Apr 8, 2024

We put the .net 8 migration work on hold due to these issues, and the lack of response to this issue (and other issues that have been posted here)
I will say that IFormFile always seems to cause issues for us, even prior to .net 8.
Sometimes it gets translated to FileParameter, other times it gets unrolled.

@awdorrin
Copy link
Author

Finally getting back to looking at this, and to expand on what I had above, the following appears to work.

public async Task<IActionResult> PutMarketStreamEnterpriseLead( 
    [BindRequired] int id,
    [FromBody] MarketStreamEnterpriseLead msel)

The [BindRequired] keeps the key field for HttpPut actions at the front of the list, while [FromBody] keeps the object from being unrolled.

Now I'm going through searching for a few thousand Put/Post methods and inserting these parameters and continuing to test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants