Repository navigation
fix(python-aiohttp, python-blueplanet, python-flask): read header/cookie params from the request; stop overwriting non-body params from the body - #25362
Conversation
…ams from request and avoid overwriting non-body model params Connexion only binds path, query, body, files, and context arguments to controller handlers and never passes header parameters, causing required header parameters in generated controller signatures to fail with TypeError at runtime. In addition, python-blueplanet and python-flask unconditionally deserialized non-container model parameters from request.get_json(), overwriting non-body model parameters such as $ref query parameters with the request body. Exclude header parameters from controller signatures via x-non-header-params and read them from request headers inside the handler, and scope request.get_json() deserialization to body parameters while deserializing non-body model parameters from their dict value. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…arams, header coercion/defaults, and non-body container params
Address Codex review findings on Connexion parameter handling:
- Scope request.get_json() deserialization for array and map parameters
in python-flask and python-blueplanet to body parameters, deserializing
non-body list/dict arguments from their passed value.
- Convert integer, number,boolean, and JSON-content header/cookie
parameters read from request headers/cookies and honor schema default
values when omitted.
- Use baseName instead of paramName for header keys in generated
controller_test.mustache across python-aiohttp, python-blueplanet, and
python-flask so required headers pass Connexion validation.
- Exclude cookie parameters from controller signatures and read them
from request.cookies.get('{{baseName}}') inside the handler.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…faults as Python literals and support array/JSON cookies - Format array schema defaults as valid Python list literals (quoting strings and capitalizing True/False for both Jackson ArrayNode and List defaults) so header/cookie array defaults do not emit bare identifiers or lowercase booleans. - Split non-JSON array header and cookie values on ',' and coerce integer/number/boolean array items, and decode JSON-content cookies via json.loads before model deserialization. - Guard non-body date, datetime, and model array comprehensions in python-aiohttp with isinstance(..., list). - Rename the x-non-header-params vendor extension to x-handler-params to reflect that both header and cookie parameters are excluded. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
There was a problem hiding this comment.
1 issue found across 13 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="modules/openapi-generator/src/main/resources/python-aiohttp/controller.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/python-aiohttp/controller.mustache:64">
P2: Header/cookie lookups leave non-JSON complex array parameters as raw strings, so valid simple-style arrays of model items never become `List[Model]`. Parse the parameter’s wire representation before the model-item conversion, or retain Connexion’s converted value.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| """ | ||
| {{#allParams}} | ||
| {{#isHeaderParam}} | ||
| {{paramName}} = request.headers.get('{{baseName}}'{{#defaultValue}}, {{{defaultValue}}}{{/defaultValue}}) |
There was a problem hiding this comment.
P2: Header/cookie lookups leave non-JSON complex array parameters as raw strings, so valid simple-style arrays of model items never become List[Model]. Parse the parameter’s wire representation before the model-item conversion, or retain Connexion’s converted value.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At modules/openapi-generator/src/main/resources/python-aiohttp/controller.mustache, line 64:
<comment>Header/cookie lookups leave non-JSON complex array parameters as raw strings, so valid simple-style arrays of model items never become `List[Model]`. Parse the parameter’s wire representation before the model-item conversion, or retain Connexion’s converted value.</comment>
<file context>
@@ -57,6 +60,44 @@ async def {{operationId}}(request: web.Request, {{#allParams}}{{paramName}}{{^re
"""
{{#allParams}}
+ {{#isHeaderParam}}
+ {{paramName}} = request.headers.get('{{baseName}}'{{#defaultValue}}, {{{defaultValue}}}{{/defaultValue}})
+ {{/isHeaderParam}}
+ {{#isCookieParam}}
</file context>
There was a problem hiding this comment.
Partially addressed in cf71763. Arrays whose items reference an enum or a primitive alias (which also have a complexType but aren't models) are now split on , like other primitive arrays before conversion.
For arrays of object models I've intentionally left the raw string alone. OpenAPI's parameter serialization (style: simple for headers, style: form for cookies) defines primitives, arrays of primitives and a single flat object, but no wire format for an array of objects; those need content: application/json, which is already decoded with json.loads before from_dict. Splitting such a value on , would produce List[str], and Model.from_dict(str) returns an empty model, which silently discards the data. Happy to change this if you'd prefer a different behavior.
… params, format nested array defaults, and split enum-ref array headers Address cubic-dev-ai review findings on PR OpenAPITools#25362: - Guard non-container date and date-time deserialization with `if <param> is not None:` across python-aiohttp, python-blueplanet, and python-flask so omitted optional date/date-time headers or cookies do not pass None into dateutil.parser.parse(). - Recursively format object and nested array elements in array schema defaults as Python dict and list literals instead of calling JsonNode.asText() (which returns '' for container nodes). - Split non-JSON array header/cookie values when items are non-object referenced schemas (such as $ref enum models where !items.isModel) while keeping object-model arrays unsplit unless JSON content is used. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ion-param-sources Picks up OpenAPITools#25372 (pytest-aiohttp loop fixture fix) so the Python Server CI samples run against current master. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ct.from_dict for free-form object params
A free-form 'type: object' (or untyped) query/header parameter has the
builtin 'object' as its type, so the non-body conversion rendered
'object.from_dict(x)'. Connexion passes {} for an absent object query
parameter, so every request to such an operation failed with
AttributeError (500). Skip the conversion when the parameter's dataType is
'object'; named models (including an allOf model without a top-level
type, which is flagged isAnyType) are still converted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion-param-sources Resolves conflicts with OpenAPITools#25361 (free-form params skip from_dict): keep the x-python-connexion-skip-from-dict guard from master on both the body and the non-body conversion, drop this branch's duplicate of that check, and update the composed-query test to expect query params to be converted from their own value rather than from the request body. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…k-param-typing Resolves conflicts with OpenAPITools#25361/OpenAPITools#25362: - postProcessConnexionOperationMap (introduced here so webhooks share the Connexion post-processing) now also receives the enclosing map, because OpenAPITools#25362 sets the hasJsonHeaderParam flag on it; this merged silently but did not compile. - The webhook controller test now expects header parameters to be read from the request (OpenAPITools#25362) instead of being handler arguments. - Keep both sets of new tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion-header-content Resolves conflicts with OpenAPITools#25362: master now splits the body and non-body model conversion; keep that structure and apply this branch's dataType fallback (for a null baseType) to the body line as well. The blueplanet JSON-header test now expects the header to be read from the request headers (OpenAPITools#25362) instead of the request body. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion-test-example-names Resolves the test-file conflict with OpenAPITools#25362 by keeping both new tests. Also covers the defaultValue path of setParameterExampleValue (cubic review): a model-typed parameter whose schema has a default keeps the default as its example instead of being turned into a constructor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xion-object-params OpenAPITools#25362 was squash-merged, so its files conflict with this branch's copy of the original commits. Master's versions of the conflicting files are identical to OpenAPITools#25362's head (cd3fe31), so this branch's versions are kept; the diff against master is now only the object-parameter commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Verified against connexion's uri_parsing (param_schema['type'] read without .get): a type-less schema KeyErrors at runtime and a 3.1 singleton type array does not equal 'object', so the author's type=='object' gate stays; the earlier acceptance is reverted. X-Obj-List is upstream's pre-existing fixture (PR OpenAPITools#25362) and is restored with its assertions. A required property with no simple/form wire value now marks the operation's generated test skipped with a reason instead of silently omitting the required parameter or sending an incomplete model. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…d header parameters (#25427) * fix(python-aiohttp, python-blueplanet, python-flask): read header params from request and avoid overwriting non-body model params Connexion only binds path, query, body, files, and context arguments to controller handlers and never passes header parameters, causing required header parameters in generated controller signatures to fail with TypeError at runtime. In addition, python-blueplanet and python-flask unconditionally deserialized non-container model parameters from request.get_json(), overwriting non-body model parameters such as $ref query parameters with the request body. Exclude header parameters from controller signatures via x-non-header-params and read them from request headers inside the handler, and scope request.get_json() deserialization to body parameters while deserializing non-body model parameters from their dict value. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(python-aiohttp, python-blueplanet, python-flask): handle cookie params, header coercion/defaults, and non-body container params Address Codex review findings on Connexion parameter handling: - Scope request.get_json() deserialization for array and map parameters in python-flask and python-blueplanet to body parameters, deserializing non-body list/dict arguments from their passed value. - Convert integer, number,boolean, and JSON-content header/cookie parameters read from request headers/cookies and honor schema default values when omitted. - Use baseName instead of paramName for header keys in generated controller_test.mustache across python-aiohttp, python-blueplanet, and python-flask so required headers pass Connexion validation. - Exclude cookie parameters from controller signatures and read them from request.cookies.get('{{baseName}}') inside the handler. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(python-aiohttp, python-blueplanet, python-flask): format array defaults as Python literals and support array/JSON cookies - Format array schema defaults as valid Python list literals (quoting strings and capitalizing True/False for both Jackson ArrayNode and List defaults) so header/cookie array defaults do not emit bare identifiers or lowercase booleans. - Split non-JSON array header and cookie values on ',' and coerce integer/number/boolean array items, and decode JSON-content cookies via json.loads before model deserialization. - Guard non-body date, datetime, and model array comprehensions in python-aiohttp with isinstance(..., list). - Rename the x-non-header-params vendor extension to x-handler-params to reflect that both header and cookie parameters are excluded. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(python-aiohttp, python-blueplanet, python-flask): guard None date params, format nested array defaults, and split enum-ref array headers Address cubic-dev-ai review findings on PR #25362: - Guard non-container date and date-time deserialization with `if <param> is not None:` across python-aiohttp, python-blueplanet, and python-flask so omitted optional date/date-time headers or cookies do not pass None into dateutil.parser.parse(). - Recursively format object and nested array elements in array schema defaults as Python dict and list literals instead of calling JsonNode.asText() (which returns '' for container nodes). - Split non-JSON array header/cookie values when items are non-object referenced schemas (such as $ref enum models where !items.isModel) while keeping object-model arrays unsplit unless JSON content is used. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(python-aiohttp, python-blueplanet, python-flask): don't call object.from_dict for free-form object params A free-form 'type: object' (or untyped) query/header parameter has the builtin 'object' as its type, so the non-body conversion rendered 'object.from_dict(x)'. Connexion passes {} for an absent object query parameter, so every request to such an operation failed with AttributeError (500). Skip the conversion when the parameter's dataType is 'object'; named models (including an allOf model without a top-level type, which is flagged isAnyType) are still converted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(python-flask, python-aiohttp): deserialize object-typed query and header parameters Connexion (2.x and 3.x) only turns style=deepObject query parameters into objects. A style=form object query parameter (?name=a&size=3) never reaches the handler, and a style=simple object header (X-Trace: id,abc) is validated as a plain string, so valid requests were rejected with 400. When a spec has object-typed query (style form or deepObject) or header (style simple) parameters whose schema is a plain 'type: object' model, the generated server now ships param_parsing.py and registers it through Connexion's uri_parser_class / validator_map hooks, the handler parses object headers (cast to the model's types), and the generated controller test sends these parameters in their OpenAPI style built from the schema examples. Specs without such parameters are generated unchanged. Composed (allOf/oneOf/anyOf) schemas and other styles keep the previous handling. python-blueplanet (Connexion 1.x, Swagger 2.0) opts out through supportsObjectParameterParsing(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(python-flask, python-aiohttp): address review of object-typed parameter support - Only handle 'type: object' models (not composed) whose properties are all scalars: style=form/simple cannot carry nested objects or arrays, so such models keep the previous handling instead of being parsed into wrong values. The Java predicate and param_parsing._is_object() check the same. - parse_simple_object() returns a malformed value unchanged (odd number of tokens, or a segment without '=' when exploded), so validation rejects it with 400 instead of silently dropping input. - Use an explicit object example (parameter example/examples, then the schema's example or default) in the generated test, whole or not at all, before falling back to values synthesized from the model's properties. ',' and '=' are only avoided where a single comma-separated value is sent. - Don't HTML-escape query keys and header/cookie names: the generated tests and the handlers' headers.get()/cookies.get() now use the name quoted for a Python literal (x-python-connexion-quoted-name), so both sides agree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Carry serialized query pairs into x-test-query-string The upstream x-test-query-string list replaced the per-param template rendering; teach the builder to expand x-python-connexion-test-query-pairs (object/array params keep their serialized pairs) and to escape the wire name (openapi-name or baseName) for regular and apiKey-in-query entries. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * Accept type-less and singleton-array object schemas A schema with properties but no explicit type, or a 3.1 singleton type array ['object'], is still an object schema; both isScalarObjectSchema and the runtime _is_object predicate now accept those forms. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * Emit no pairs for objects with no serializable scalar properties An intentionally empty x-python-connexion-test-query-pairs list is now honored: the param is omitted from the generated test's query list instead of falling back to the raw example value. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * Drop unencodable array-of-object header param and guard nested props OpenAPI defines no simple-style serialization for an array of objects, so the generated from_dict loop could never parse such a header; the fixture entry and its generation assertions are removed. Object params with a required non-scalar property now produce an empty pair list so generated tests omit the param instead of sending an incomplete model. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * Keep strict object-type gate, restore X-Obj-List, skip unencodable tests Verified against connexion's uri_parsing (param_schema['type'] read without .get): a type-less schema KeyErrors at runtime and a 3.1 singleton type array does not equal 'object', so the author's type=='object' gate stays; the earlier acceptance is reverted. X-Obj-List is upstream's pre-existing fixture (PR #25362) and is restored with its assertions. A required property with no simple/form wire value now marks the operation's generated test skipped with a reason instead of silently omitting the required parameter or sending an incomplete model. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * Render test query-pair names unescaped in flask and aiohttp tests Pair keys are already escaped for a single-quoted Python literal in Java (escapeTextInSingleQuotes), so {{name}} HTML-escapes them a second time: a deepObject key like amp[a&b] rendered as amp[a&b] and the generated test no longer matched the wire name. Use {{{name}}} as in the other string interpolations; fixes the Windows Build failure in PythonFlaskConnexionServerCodegenTest.testObjectTypedQueryAndHeaderParameters. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
In the Connexion-based Python server generators (python-aiohttp, python-blueplanet, python-flask), generated handlers declare
in: headerandin: cookieparameters as function arguments. Connexion (1.x, 2.x and 3.x) never injects those: only path, query, body and files arguments are passed (see the request handling docs, 2.14.2connexion/operations/abstract.py#get_argumentsand 3.xconnexion/decorators/parameter.py). As a result:TypeError: <handler>() missing 1 required positional argument→ HTTP 500. Reproduced with real servers on Connexion 3.3.0 and 2.14.2 (python-flask), 2.14.2 (python-aiohttp) and 1.1.15 (python-blueplanet).None, even when the client sends it (for example,api_keyin the petstoredelete_pet).Separately, python-flask and python-blueplanet deserialize non-body model and container parameters from
request.get_json(), the request body. A$refquery parameter sent alongside an unrelated JSON body is overwritten by that body.Changes
x-handler-paramslists the remaining ones). They are read inside the handler fromrequest.headers/request.cookies(flask.request,connexion.requestor aiohttp'srequest) by their original name.baseNameis restored fromx-python-connexion-openapi-name.,and their items coerced.content: application/jsonheaders and cookies are decoded withjson.loadsbeforeModel.from_dict.get_json()-based conversion is limited to body parameters. Non-body model and container parameters are converted from the value Connexion passed.['foo', 'bar'],[True]). This override oftoDefaultValuealso affects model__init__defaults: for example, the 7.26.0 outputflags=[true]raisesNameErrorwhen the model is imported, and now becomes[True].baseName(X-Req-Header), not the Python parameter name.Migration note
For operations with header or cookie parameters, the generated handler signature changes. Those parameters are no longer function arguments and are read from the request inside the handler. Code that calls generated handlers directly and passes header or cookie arguments needs to provide them through the request instead. Before this change, Connexion never passed these arguments anyway: required ones failed and optional ones were always
None.Tests and verification
src/test/resources/3_0/python/connexion-param-sources.yaml. New tests inAbstractPythonConnexionServerCodegenTestcover all three generators plususeConnexion3. Each fails without its fix.Python*Test,AbstractPython*Test: 164 tests, 0 failures. Samples change only in the four petstore serverpet_controller.pyfiles, wheredelete_pet'sapi_keyheader is now read from the request.400 Missing header parameter. The generatedtest_default_controller.pypasses with pytest for a spec with a required header.Known limitations (not changed here)
type: integer/number/booleanheader values such as'42'with a 400 before the handler runs. This is Connexion behavior, not something the generator controls.openapi_server.<Model>()without importing it (for example,samples/server/petstore/python-flask/openapi_server/test/test_store_controller.py), so those tests fail withNameError. This was already the case in 7.26.0.deepObjectquery parameters as raw strings, so they aren't converted to models.Note on overlap
This touches the same controller-template blocks and
AbstractPythonConnexionServerCodegenTestas #25352 and #25361. Whichever lands first, I'll rebase the others.PR checklist
cc @cbornet @tomplus @krjakbrjak @fa0311 (Python technical committee)
Parts of this change were drafted with AI coding assistants (Devin, Claude Code) and reviewed and verified by me.
🤖 Generated with Claude Code
Summary by cubic
Fixes header and cookie parameter handling in the
python-aiohttp,python-blueplanet, andpython-flaskgenerators so generated servers receive these values instead of crashing or ignoring them.$refquery parameters) are no longer overwritten from the request body; only body parameters userequest.get_json().baseName.Migration
Written for commit cf71763. Summary will update on new commits.