Shaddix / react-query-swagger

Generates react-query hooks based on Swagger API definitions
MIT License
122 stars 4 forks source link

Support for key-value query parameters #25

Closed Rudomitori closed 1 year ago

Rudomitori commented 1 year ago

It will be very handy to support complex parameters in some cases. E.g. if you have ASP.NET Core application with an endpoint that receives Dictionary<string, string> from query parameters. Although react-query-swagger has support for JSON-serialized parameters and it can be used for complex parameters but ASP.NET Core doesn't support it out of the box.

Now if you try to generate client with the following command and swagger.json:

react-query-swagger openapi2tsclient /tanstack /input:swagger.json /output:api-client.ts /template:Axios /serviceHost:. /minimal
{
  "openapi": "3.0.0",
  "info": {
    "title": "Bug reproduction",
    "description": "Bug reproduction",
    "version": "1"
  },
  "paths": {
    "/api/v1/Answers": {
      "get": {
        "tags": [
          "Answers"
        ],
        "operationId": "Answers_GetAnswers",
        "parameters": [
          {
            "name": "Tags",
            "in": "query",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    }
  }
}

You get the following TypeScript code in api-client/AnswersQuery.ts and api-client/AnswersClient.ts:

    let url_ = getBaseUrl() + "/api/v1/Answers?";
    if (tags === null)
        throw new Error("The parameter 'tags' cannot be null.");
    else if (tags !== undefined)
        url_ += "Tags=" + encodeURIComponent("" + tags) + "&";
      url_ = url_.replace(/[?&]$/, "");

Here's the problem with encodeURIComponent("" + tags) because encodeURIComponent("" + {"key": "value"}) => encodeURIComponent("[object Object]")

I think, according to the OpenAPI docs, there should be the following code here:

for (const key of Object.getOwnPropertyNames(tags)) {
    url_ += encodeURIComponent(key) + '=' + encodeURIComponent(tags[key]) + '&';
}

Alos I'll appreciate if you implement support for "deepObject" parameter serialization style.

Shaddix commented 1 year ago

Should be fixed in 15.7.10

Rudomitori commented 1 year ago

Thanks for the fix!

I've tested it and found out that now for the same given command and swagger the following code is generated:

tags && Object.keys(tags).forEach(key => { url_ += `Tags[${key}]=` + encodeURIComponent("" + tags[key]) + "&"; });

I see here 2 issues:

  1. A key is not encoded and it leads to that if Tags = {"complex=key":"value"} then ASP.NET Core cannot correctly parse the query params.
  2. The generated code corresponds to "deepObject" parameter serialization style but in the given swagger style is not specified and a default one should be used.

This is example of a swagger with a specified serialization style:

{
  "openapi": "3.0.0",
  "info": {
    "title": "Bug reproduction",
    "description": "Bug reproduction",
    "version": "1"
  },
  "paths": {
    "/api/v1/Answers": {
      "get": {
        "tags": [
          "Answers"
        ],
        "operationId": "Answers_GetAnswers",
        "parameters": [
          {
            "name": "Tags",
            "in": "query",
            "style": "deepObject",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    }
  }
}
Shaddix commented 1 year ago

yep, I misunderstood the spec, thanks! gonna fix soon

Shaddix commented 1 year ago

Should be fixed in 15.18.2

Shaddix commented 1 year ago

Alos I'll appreciate if you implement support for "deepObject" parameter serialization style.

That would definitely be good (better spec support is always good), but let me ask you a question. How do you plan to use it? I mean, currently you won't be able to correctly consume this deepObject by ASP.NET Core Controller, right?

Rudomitori commented 1 year ago

deepObject serialization style is required when I try write endpoint like this:

[HttpGet]
public async Task<PagedResult<AnswerDto>> GetAnswers([FromQuery] AnswerFilters filters)  {
    // Do something
};

public class AnswerFilters {
    public DateTime? Since { get; set; }
    public DateTime? Until { get; set; }

    public Dictionary<string, string>? Tags { get; set; }
}

Here Tags cannot be bound if the query parameter isn't serialized with deepObject style.

Shaddix commented 1 year ago

yep, that makes sense. I've created another issue for handling this scenario, I suppose we can consider this issue closed, right?

Rudomitori commented 1 year ago

Yes. Thanks for library improvements!