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

[REQ] [csharp] [generichost] Support query parameters with style: deepObject and explode: true #19970

Open
leorg99 opened this issue Oct 24, 2024 · 1 comment

Comments

@leorg99
Copy link

leorg99 commented Oct 24, 2024

Is your feature request related to a problem? Please describe.

The OpenAPI spec allows you to define parameters that can be rendered as color[R]=100&color[G]=200&color[B]=150.

This can be written in the OpenApi yaml file as follows:

parameters:
  - name: color
    in: query
    style: deepObject
    explode: true
    schema:
      title: ColorParameter
      type: object
      properties:
        key:
          type: string
        value:
          type: string

This is related to the issue in #19893

Describe the solution you'd like

To implement this, we need to modify several files.

Dealing with explode

First, we need to fix the signature of parameters with 'explode: true'.
The spec states that when this is true:

Parameter values of type array or object generate separate parameters for each value of the array or key-value pair of the map.

For a query parameter with name: color, this would look like

/foo&color=blue&color=black&color=orange

Since the parameter can be used 0 or more times, we need to change the parameter to a collection type (like List<T>).
This can be done by modifying Operation.Signature.mustache to add a {{#isExplode}} ... {{/isExplode}} section.

Next, we have to modify the following to add all the values in the List<T> parameter to NameValueCollection

System.Collections.Specialized.NameValueCollection parseQueryStringLocalVar = System.Web.HttpUtility.ParseQueryString(string.Empty);
{{/-first}}
{{/queryParams}}
{{^queryParams}}
{{#authMethods}}
{{#isApiKey}}
{{#isKeyInQuery}}
System.Collections.Specialized.NameValueCollection parseQueryStringLocalVar = System.Web.HttpUtility.ParseQueryString(string.Empty);
{{/isKeyInQuery}}
{{/isApiKey}}
{{/authMethods}}
{{/queryParams}}
{{#queryParams}}
{{#required}}
{{#-first}}
{{/-first}}
parseQueryStringLocalVar["{{baseName}}"] = ClientUtils.ParameterToString({{paramName}});
{{/required}}
{{/queryParams}}
{{#constantParams}}
{{#isQueryParam}}
// Set client side default value of Query Param "{{baseName}}".
parseQueryStringLocalVar["{{baseName}}"] = ClientUtils.ParameterToString({{#_enum}}"{{{.}}}"{{/_enum}}); // Constant query parameter
{{/isQueryParam}}
{{/constantParams}}
{{#queryParams}}
{{^required}}
if ({{paramName}}.IsSet)
parseQueryStringLocalVar["{{baseName}}"] = ClientUtils.ParameterToString({{paramName}}.Value);
{{/required}}
{{#-last}}
uriBuilderLocalVar.Query = parseQueryStringLocalVar.ToString();

Support deepObject style

This along with explode:true allows us to specify query parameters as someParam[field1]=value1&someParam[field2]=value2&someParam[field3]=value3

Since the parameter defines a schema with type: object and properties, the generator generates a model to represent this type. For deepObject styles, it only makes sense to define two properties: the field or column and the value. I think what we can do here is then generate a model that has

// from parameter.name
public string Name {get; set;}

// from parameter.schema.properties[0]
public string Column {get; set;}  // or Field

// from parameter.schema.properties[1]
public string Value {get; set;}

Then we can generate the query parameter as Name[Column]=Value

Building on top of the changes we made previously,

Describe alternatives you've considered

Additional context

@leorg99
Copy link
Author

leorg99 commented Oct 24, 2024

@devhl-labs

As it relates to #19893, I wrote a small script to transformed the filter* query parameters to style: deepObject , explode: true, and replaced the schema to have two properties.

output

using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
using Microsoft.OpenApi.Writers;

OpenApiDocument document;

using (var httpClient = new HttpClient())
{
    var stream = await httpClient.GetStreamAsync("https://app.files.com/api/rest/v1/swagger_doc.json");
    var reader = new OpenApiStreamReader();
    document = reader.Read(stream, out var diagnostic);
}

foreach (var (path, item) in document.Paths)
{
    foreach (var (operationType, operation) in item.Operations)
    {
        for (var i = 0; i < operation.Parameters.Count; i++)
        {
            var parameter = operation.Parameters[i];

            if (parameter.Name.StartsWith("filter"))
            {
                parameter.Style = ParameterStyle.DeepObject;
                parameter.Explode = true;
                parameter.Schema = new OpenApiSchema
                {
                    Title = "FilterParameter",
                    Type = "object",
                    Properties = new Dictionary<string, OpenApiSchema>()
                    {
                        ["field"] = new OpenApiSchema() { Type = "string" },
                        ["value"] = new OpenApiSchema() { Type = "string" }
                    }
                };
            }
        }
    }

    // Serialize and save the modified OpenAPI document to a new file
    await using var streamWriter = new StreamWriter("updated.yaml");
    var writer = new OpenApiYamlWriter(streamWriter);
    document.SerializeAsV3(writer);
}

@leorg99 leorg99 changed the title [REQ] Support query parameters with style: deepObject and explode: true [REQ] [csharp] [generichost] Support query parameters with style: deepObject and explode: true Oct 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant