OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.61k stars 6.52k forks source link

[BUG] csharp generichost issue with generated sort and filter query parameters #19893

Open leorg99 opened 5 hours ago

leorg99 commented 5 hours ago

Bug Report Checklist

Description

I am not able to specify a sort or filter query parameter in the required form using the generated code.

I am not sure if it's a bug in the generator or simply a limitation of the OpenAPI V2 Definition File or the spec. If this is the case, then maybe there is a work around that I can implement?

Background:

The API that I am working with shows that sort and filter can be used as follows:

sort_by[<field>]=<value>

where:  \<field> is the name of the field to sort by, placed within square brackets []  \<value> is asc or desc.

<filter>[<field>]=<value>

where:  \<filter> is the filter type to use;  \<field>is the name of the field to filter by, placed within square brackets [];  \<value> is the field value that the filter should apply to.

As an example, this is how the sort and filter is generated for the following method in GroupsApi.cs:

public async Task<IGetGroupsApiResponse> GetGroupsAsync(Option<string> cursor = default, Option<int> perPage = default, Option<Object> sortBy = default, Option<Object> filter = default, Option<Object> filterPrefix = default, Option<string> ids = default, System.Threading.CancellationToken cancellationToken = default)
{
  ...
     System.Collections.Specialized.NameValueCollection parseQueryStringLocalVar = System.Web.HttpUtility.ParseQueryString(string.Empty);

   if (cursor.IsSet)
       parseQueryStringLocalVar["cursor"] = ClientUtils.ParameterToString(cursor.Value);

   if (perPage.IsSet)
       parseQueryStringLocalVar["per_page"] = ClientUtils.ParameterToString(perPage.Value);

   if (sortBy.IsSet)
       parseQueryStringLocalVar["sort_by"] = ClientUtils.ParameterToString(sortBy.Value);

   if (filter.IsSet)
       parseQueryStringLocalVar["filter"] = ClientUtils.ParameterToString(filter.Value);

   if (filterPrefix.IsSet)
       parseQueryStringLocalVar["filter_prefix"] = ClientUtils.ParameterToString(filterPrefix.Value);

   if (ids.IsSet)
       parseQueryStringLocalVar["ids"] = ClientUtils.ParameterToString(ids.Value);

   uriBuilderLocalVar.Query = parseQueryStringLocalVar.ToString();

   httpRequestMessageLocalVar.RequestUri = uriBuilderLocalVar.Uri;
...
}

The OpenAPI V2 Definition File defines this method as follows:

    "/groups": {
      "get": {
        "summary": "List Groups",
        "description": "List Groups",
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "in": "query",
            "name": "cursor",
            "description": "Used for pagination.  When a list request has more records available, cursors are provided in the response headers `X-Files-Cursor-Next` and `X-Files-Cursor-Prev`.  Send one of those cursor value here to resume an existing list from the next available record.  Note: many of our SDKs have iterator methods that will automatically handle cursor-based pagination.",
            "type": "string",
            "required": false,
            "x-ms-summary": "Used for pagination.  When a list request has more records available, cursors are provided in the response headers `X-Files-Cursor-Next` and `X-Files-Cursor-Prev`.  Send one of those cursor value here to resume an existing list from the next available record.  Note: many of our SDKs have iterator methods that will automatically handle cursor-based pagination."
          },
          {
            "in": "query",
            "name": "per_page",
            "description": "Number of records to show per page.  (Max: 10,000, 1,000 or less is recommended).",
            "type": "integer",
            "format": "int32",
            "required": false,
            "x-ms-summary": "Number of records to show per page.  (Max: 10,000, 1,000 or less is recommended)."
          },
          {
            "in": "query",
            "name": "sort_by",
            "description": "If set, sort records by the specified field in either `asc` or `desc` direction. Valid fields are `name`.",
            "type": "object",
            "required": false,
            "x-example": {
              "name": "desc"
            },
            "x-ms-summary": "If set, sort records by the specified field in either `asc` or `desc` direction. Valid fields are `name`."
          },
          {
            "in": "query",
            "name": "filter",
            "description": "If set, return records where the specified field is equal to the supplied value. Valid fields are `name`.",
            "type": "object",
            "required": false,
            "x-example": {
              "name": "test"
            },
            "x-ms-summary": "If set, return records where the specified field is equal to the supplied value. Valid fields are `name`."
          },
          {
            "in": "query",
            "name": "filter_prefix",
            "description": "If set, return records where the specified field is prefixed by the supplied value. Valid fields are `name`.",
            "type": "object",
            "required": false,
            "x-example": {
              "name": "test"
            },
            "x-ms-summary": "If set, return records where the specified field is prefixed by the supplied value. Valid fields are `name`."
          },
          {
            "in": "query",
            "name": "ids",
            "description": "Comma-separated list of group ids to include in results.",
            "type": "string",
            "required": false,
            "x-ms-summary": "Comma-separated list of group ids to include in results."
          }
        ],
        "responses": {
          "200": {
            "description": "A list of Groups objects.",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/GroupEntity"
              }
            },
            "x-ms-summary": "A list of Groups objects."
          },
          "400": {
            "description": "Bad Request",
            "x-ms-summary": "Bad Request"
          },
          "401": {
            "description": "Unauthorized",
            "x-ms-summary": "Unauthorized"
          },
          "403": {
            "description": "Forbidden",
            "x-ms-summary": "Forbidden"
          },
          "404": {
            "description": "Not Found",
            "x-ms-summary": "Not Found"
          },
          "405": {
            "description": "Method Not Allowed",
            "x-ms-summary": "Method Not Allowed"
          },
          "409": {
            "description": "Conflict",
            "x-ms-summary": "Conflict"
          },
          "412": {
            "description": "Precondition Failed",
            "x-ms-summary": "Precondition Failed"
          },
          "422": {
            "description": "Unprocessable Entity",
            "x-ms-summary": "Unprocessable Entity"
          },
          "423": {
            "description": "Locked",
            "x-ms-summary": "Locked"
          },
          "429": {
            "description": "Too Many Requests",
            "x-ms-summary": "Too Many Requests"
          }
        },
        "tags": [
          "groups",
          "Groups"
        ],
        "operationId": "GetGroups",
        "x-authentication": [
          "folder_admin"
        ],
        "x-category": [
          "user_accounts"
        ],
        "x-included-in-ipaas": true,
        "x-sortable_columns": [
          "name"
        ],
        "x-filter_sort_combinations_with_types": {
          "filter_columns": {
            "name": {
              "type": "pattern",
              "sort": [
                "name"
              ]
            }
          },
          "filter_combinations": []
        }
      }
    },
openapi-generator version

7.9.0

OpenAPI declaration file content or url
Generation Details

csharp.config

{
    "apiName": "FilesComApi",
    "library": "generichost",
    "netCoreProjectFile": true,
    "nullableReferenceTypes": true,
    "optionalAssemblyInfo": true,
    "packageName": "Files.Com.Core",
    "targetFramework": "net8.0",
    "sourceFolder": "src"
}
Steps to reproduce

Build

cd generated
dotnet new globaljson --sdk-version 8.0.400 --roll-forward feature
dotnet build
Related issues/PRs

19626

19892

Suggest a fix
leorg99 commented 5 hours ago

Thanks in advance! @devhl-labs @wing328

devhl-labs commented 3 hours ago

I don't believe this is a part of the openapi spec. To get some support for it, you may need to manually edit the spec before using it, or ask the api owners for changes.