Code generation fails when Response is reused as Request

When a DTO object is used both a a request and a response the generated code treats it as just a response and not a request.

For example:

[Route("/thing/{Id}", verbs: "GET")]
public class ThingRequest : IReturn<Thing>
{
    public int Id { get; set; }
}

[Route("/thing/{Id}", verbs: "PUT")]
public class Thing : IReturn<Thing>
{
    public int Id { get; set; }
    public string Value { get; set; }
}

When the client code is generated to route on Thing is missing.
This is the C# code generated (but it seems to affect all languages):

public partial class Thing
{
    public virtual int Id { get; set; }
    public virtual string Value { get; set; }
}

[Route("/thing/{Id}", "GET")]
public partial class ThingRequest
    : IReturn<Thing>
{
    public virtual int Id { get; set; }
}

This issue seems to be caused here: https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack/NativeTypes/NativeTypesMetadata.cs#L124
The Thing type is added to the opTypes when ThingRequest operation is handled, causing the Thing operation to be skipped leaving us with a Thing without routes.

Should now be resolved from this commit.

This change is available from v5.10.5 that’s now available on MyGet.

I grabbed that one, and it does indeed fix this case, so that’s good. Thanks for that.

However there seems to be a similar issue left when AutoQuery is being used, specifically for Typescript. (C# code is correct, I haven’t checked the others.)

These requests give me the same issue where the Thing is generated as just a response:

[Route("/thing/{Id}", verbs: "GET")]
public class ThingRequest : QueryDb<Thing>
{
    public int Id { get; set; }
}

[Route("/thing/{Id}", verbs: "PUT")]
public class Thing : ISaveDb<Thing>, IReturnVoid
{
    public int Id { get; set; }
    public string Value { get; set; }
}

I debugged this down to https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack/NativeTypes/TypeScript/TypeScriptGenerator.cs#L242.

When we get there AllTypes contains the Thing twice, and the first one happens to be the response so that gets generated, causing the request gets skipped. This was what the some debugging showed me at that point:

I’m unsure if the error is in this code, or in the fact that “Thing” is added to AllTypes twice…

This is the generated TypeScript code those C# DTOs generate:

// @Route("/thing/{Id}", "GET")
export class ThingRequest extends QueryDb_1<Thing> implements IReturn<QueryResponse<Thing>>
{
    public id: number;

    public constructor(init?: Partial<ThingRequest>) { super(init); (Object as any).assign(this, init); }
    public createResponse() { return new QueryResponse<Thing>(); }
    public getTypeName() { return 'ThingRequest'; }
}

export class Thing implements ISaveDb<Thing>
{
    public id: number;
    public value: string;

    public constructor(init?: Partial<Thing>) { (Object as any).assign(this, init); }
}

Ok seeing that it’s not generated like a Request DTO.

The point is that Thing actually is a Request as well here. So I’d expect the typescript class for Thing to contain getTypeName() and createResponse() methods too, allowing the client to PUT it…

Yes I can see that, still investigating…

Ok, I’ll shut up and let you do your thing :wink:

Ok removing the duplicate seems to have resolved the issue, now available in the latest v5.10.5 that’s now on MyGet.

As you already have v5.10.5 installed you’ll need to clear your NuGet packages cache to download the latest version, e.g:

$ nuget locals all -clear

Looks like it worked indeed. I just ran the code generation on the actual code and it looks a lot better
Thanks! :+1:

1 Like