[Python] boolean query parameter capitalization
(Copy of https://github.com/swagger-api/swagger-codegen/issues/8433)
Description
The generated python client translates boolean query parameters in Python case - i.e. "True"/"False" rather than "true"/"false".
I am dealing with a case-sensitive API endpoint, which expects a boolean query parameter, and treats anything except "0" and "false" as true - including "False" evaluating to true.
Per RFC 3986, the query part of a URI should be treated as case-sensitive. I suspect many (most?) servers in the wild expect "false", in line with the variable names in most languages.
Is this expected behavior?
Swagger-codegen version
2.4.0-SNAPSHOT
Swagger declaration file content or url
"parameters": [
{
"name": "reverse",
"in": "query",
"description": "If true, will sort results newest first.",
"required": false,
"default": false,
"type": "boolean"
}
],
Command line used for generation
java -jar ./swagger-codegen/modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate -i ./swagger.json -l python -o generated/python
Steps to reproduce
# given an API endpoint which accepts a boolean query parameter
>>> foo_list = client.Foo.Foo_get(reverse=True)
>>> foo_list.future.request.prepare()
>>> print(foo_list.url)
http://example.com/api/foo?reverse=True
Suggest a fix/enhancement
Suggestion: instead of "True" or "False", convert to the string "true" or "false".
@ryanfox one workaround is to document the query parameter as a string instead of boolean and users will need to input either "true" or "false"
Another way is to use customized templates with the -t option and add logic to normalize the values inside the following mustache tag:
{{#isBoolean}}
...
{{/isBoolean}}
Thanks for the info. That's not going to work for the use case I am working with - what do you think the chances are of implementing a fix?
In any case, good to know there is a workaround.
@ryanfox why would the string solution not work in your use case? Why not make it a string enum?
- name: "reverse"
in: "query"
description: 'If true, will sort results newest first.'
required: false
default: "false"
type: "string"
enum:
- "true"
- "false"
@spacether Perhaps because it adds a lot of noise to the spec, lies to every tool involved including the API generator, and generally just smells of a massive, dirty hack? :))
Not saying that it shouldn't be fixed. My goal was to provide solution that unblocks Ryan. We welcome any PRs that fix issues. Do you want to work on fixing this @ryanfox or @mcejp ?
For context here, I have yet to see an RFC that:
- allows booleans as query parameters
- defines what the behavior should be
If anyone knows of one, please do let me and the other maintainers know.
So as far as I can see, this requested behavior has no generally agreed upon RFC spec defining boolean query parameter behavior.
one could send ?someVal (presence) or ?someVal=true (pass by value)
Note: If one sent someVal=true it is unclear if someVal has a boolean or a string value if the deserializing schema allows type string and boolean types.
The relevant rfc that I know is RFC 6570
Related issues:
- https://github.com/OpenAPITools/openapi-generator/issues/11683
- https://github.com/OpenAPITools/openapi-generator/issues/10787
- https://github.com/OpenAPITools/openapi-generator/issues/9709
@ryanfox one workaround is to document the query parameter as a string instead of boolean and users will need to input either "true" or "false"
Another way is to use customized templates with the
-toption and add logic to normalize the values inside the following mustache tag:{{#isBoolean}} ... {{/isBoolean}}
@wing328 I would be very interested for a patch for the template
FYI a quick & dirty (or not) way after generating templates with
java -jar openapi-generator-cli-6.0.1.jar author template -g python --library webclient
i don't know what --library webclient really represent or anyother option...
I patched api_client.mustache like so in def sanitize_for_serialization(cls, obj):
# bool first because a bool is also an int...
elif isinstance(obj, bool):
return obj.__str__().lower()
elif isinstance(obj, (str, int, float, none_type)):