Skip to content

fix: Allow parameters named "client" and "url" #765

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

Merged
merged 3 commits into from
May 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import types

from . import get_common_parameters, post_common_parameters
from . import get_common_parameters, post_common_parameters, reserved_parameters


class DefaultEndpoints:
Expand All @@ -13,3 +13,7 @@ def get_common_parameters(cls) -> types.ModuleType:
@classmethod
def post_common_parameters(cls) -> types.ModuleType:
return post_common_parameters

@classmethod
def reserved_parameters(cls) -> types.ModuleType:
return reserved_parameters
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
from http import HTTPStatus
from typing import Any, Dict, Optional

import httpx

from ... import errors
from ...client import Client
from ...types import UNSET, Response


def _get_kwargs(
*,
client: Client,
client_query: str,
url_query: str,
) -> Dict[str, Any]:
url = "{}/naming/reserved-parameters".format(client.base_url)

headers: Dict[str, str] = client.get_headers()
cookies: Dict[str, Any] = client.get_cookies()

params: Dict[str, Any] = {}
params["client"] = client_query

params["url"] = url_query

params = {k: v for k, v in params.items() if v is not UNSET and v is not None}

return {
"method": "get",
"url": url,
"headers": headers,
"cookies": cookies,
"timeout": client.get_timeout(),
"follow_redirects": client.follow_redirects,
"params": params,
}


def _parse_response(*, client: Client, response: httpx.Response) -> Optional[Any]:
if response.status_code == HTTPStatus.OK:
return None
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None


def _build_response(*, client: Client, response: httpx.Response) -> Response[Any]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)


def sync_detailed(
*,
client: Client,
client_query: str,
url_query: str,
) -> Response[Any]:
"""
Args:
client_query (str):
url_query (str):

Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns:
Response[Any]
"""

kwargs = _get_kwargs(
client=client,
client_query=client_query,
url_query=url_query,
)

response = httpx.request(
verify=client.verify_ssl,
**kwargs,
)

return _build_response(client=client, response=response)


async def asyncio_detailed(
*,
client: Client,
client_query: str,
url_query: str,
) -> Response[Any]:
"""
Args:
client_query (str):
url_query (str):

Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns:
Response[Any]
"""

kwargs = _get_kwargs(
client=client,
client_query=client_query,
url_query=url_query,
)

async with httpx.AsyncClient(verify=client.verify_ssl) as _client:
response = await _client.request(**kwargs)

return _build_response(client=client, response=response)
29 changes: 29 additions & 0 deletions end_to_end_tests/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,35 @@
}
}
},
"/naming/reserved-parameters": {
"description": "Ensure that parameters can't be named things that the code generator needs as variables",
"get": {
"operationId": "reserved-parameters",
"parameters": [
{
"name": "client",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "url",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/parameter-references/{path_param}": {
"get": {
"tags": [
Expand Down
9 changes: 7 additions & 2 deletions openapi_python_client/parser/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ..utils import PythonIdentifier, get_content_type
from .errors import GeneratorError, ParseError, PropertyError
from .properties import (
AnyProperty,
Class,
EnumProperty,
ModelProperty,
Expand Down Expand Up @@ -346,11 +347,15 @@ def add_parameters(
endpoint = deepcopy(endpoint)

unique_parameters: Set[Tuple[str, oai.ParameterLocation]] = set()
parameters_by_location = {
parameters_by_location: Dict[str, Dict[str, Property]] = {
oai.ParameterLocation.QUERY: endpoint.query_parameters,
oai.ParameterLocation.PATH: endpoint.path_parameters,
oai.ParameterLocation.HEADER: endpoint.header_parameters,
oai.ParameterLocation.COOKIE: endpoint.cookie_parameters,
"RESERVED": { # These can't be param names because codegen needs them as vars, the properties don't matter
"client": AnyProperty("client", True, False, None, PythonIdentifier("client", ""), None, None),
"url": AnyProperty("url", True, False, None, PythonIdentifier("url", ""), None, None),
},
}

for param in data.parameters:
Expand Down Expand Up @@ -412,7 +417,7 @@ def add_parameters(
continue
existing_prop: Property = parameters_dict[prop.name]
# Existing should be converted too for consistency
endpoint.used_python_identifiers.remove(existing_prop.python_name)
endpoint.used_python_identifiers.discard(existing_prop.python_name)
existing_prop.set_python_name(new_name=f"{existing_prop.name}_{location}", config=config)

if existing_prop.python_name in endpoint.used_python_identifiers:
Expand Down