Skip to content

Commit 6bc9542

Browse files
committed
Merge branch 'main' of https://github.com/triaxtec/openapi-python-client into feature/add-unset-value-and-model-to-dict-params
2 parents 9e9ebbe + 7a483dc commit 6bc9542

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1325
-104
lines changed

.github/dependabot.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: pip
4+
directory: "/"
5+
schedule:
6+
interval: daily
7+
open-pull-requests-limit: 10

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ dmypy.json
1919
# JetBrains
2020
.idea/
2121

22+
test-reports/
23+
2224
/coverage.xml
2325
/.coverage
2426
htmlcov/
2527

2628
# Generated end to end test data
27-
my-test-api-client
29+
my-test-api-client

CHANGELOG.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,25 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## Unreleased
8+
## 0.7.0 - Unreleased
99

10-
### Changes
10+
### Additions
11+
12+
- Added a `--custom-template-path` option for providing custom jinja2 templates (#231 - Thanks @erichulburd!).
13+
14+
## 0.6.2 - 2020-11-03
1115

1216
### Fixes
1317

1418
- Prefix generated identifiers to allow leading digits in field names (#206 - @kalzoo).
19+
- Prevent autoflake from removing `__init__.py` imports during generation. (#223 - Thanks @fyhertz!)
20+
- Update minimum Pydantic version to support Python 3.9
1521

1622
### Additions
1723
- Better compatibility for "required" (whether or not the field must be included) and "nullable" (whether or not the field can be null) (#205 & #208). Thanks @bowenwr & @emannguitar!
1824

25+
- Allow specifying the generated client's version using `package_version_override` in a config file. (#225 - Thanks @fyhertz!)
26+
1927
## 0.6.1 - 2020-09-26
2028

2129
### Changes

CODE_OF_CONDUCT.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,22 @@ appearance, race, religion, or sexual identity and orientation.
1414
Examples of behavior that contributes to creating a positive environment
1515
include:
1616

17-
* Using welcoming and inclusive language
18-
* Being respectful of differing viewpoints and experiences
19-
* Gracefully accepting constructive criticism
20-
* Focusing on what is best for the community
21-
* Showing empathy towards other community members
17+
- Using welcoming and inclusive language
18+
- Being respectful of differing viewpoints and experiences
19+
- Gracefully accepting constructive criticism
20+
- Focusing on what is best for the community
21+
- Showing empathy towards other community members
2222

2323
Examples of unacceptable behavior by participants include:
2424

25-
* The use of sexualized language or imagery and unwelcome sexual attention or
26-
advances
27-
* Trolling, insulting/derogatory comments, and personal or political attacks
28-
* Public or private harassment
29-
* Publishing others' private information, such as a physical or electronic
30-
address, without explicit permission
31-
* Other conduct which could reasonably be considered inappropriate in a
32-
professional setting
25+
- The use of sexualized language or imagery and unwelcome sexual attention or
26+
advances
27+
- Trolling, insulting/derogatory comments, and personal or political attacks
28+
- Public or private harassment
29+
- Publishing others' private information, such as a physical or electronic
30+
address, without explicit permission
31+
- Other conduct which could reasonably be considered inappropriate in a
32+
professional setting
3333

3434
## Our Responsibilities
3535

README.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
[![triaxtec](https://circleci.com/gh/triaxtec/openapi-python-client.svg?style=svg)](https://circleci.com/gh/triaxtec/openapi-python-client)
22
[![codecov](https://codecov.io/gh/triaxtec/openapi-python-client/branch/main/graph/badge.svg)](https://codecov.io/gh/triaxtec/openapi-python-client)
3-
[![PyPI version shields.io](https://img.shields.io/pypi/v/openapi-python-client.svg)](https://pypi.python.org/pypi/openapi-python-client/)
43
[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/)
54
[![Generic badge](https://img.shields.io/badge/type_checked-mypy-informational.svg)](https://mypy.readthedocs.io/en/stable/introduction.html)
65
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
6+
[![PyPI version shields.io](https://img.shields.io/pypi/v/openapi-python-client.svg)](https://pypi.python.org/pypi/openapi-python-client/)
7+
[![Downloads](https://static.pepy.tech/personalized-badge/openapi-python-client?period=total&units=international_system&left_color=blue&right_color=green&left_text=Downloads)](https://pepy.tech/project/openapi-python-client)
78

89
# openapi-python-client
910

@@ -53,6 +54,18 @@ get an error.
5354

5455
> For more usage details run `openapi-python-client --help` or read [usage](usage.md)
5556
57+
### Using custom templates
58+
59+
This feature leverages Jinja2's [ChoiceLoader](https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.ChoiceLoader) and [FileSystemLoader](https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.FileSystemLoader). This means you do _not_ need to customize every template. Simply copy the template(s) you want to customize from [the default template directory](openapi_python_client/templates) to your own custom template directory (file names _must_ match exactly) and pass the template directory through the `custom-template-path` flag to the `generate` and `update` commands. For instance,
60+
61+
```
62+
openapi-python-client update \
63+
--url https://my.api.com/openapi.json \
64+
--custom-template-path=relative/path/to/mytemplates
65+
```
66+
67+
_Be forewarned, this is a beta-level feature in the sense that the API exposed in the templates is undocumented and unstable._
68+
5669
## What You Get
5770

5871
1. A `pyproject.toml` file with some basic metadata intended to be used with [Poetry].
@@ -114,14 +127,24 @@ package_name_override: my_extra_special_package_name
114127

115128
### field_prefix
116129

117-
When generating properties, the `name` attribute of the OpenAPI schema will be used. When the `name` is not a valid
118-
Python identifier (e.g. begins with a number) this string will be prepended. Defaults to "field_".
130+
When generating properties, the `name` attribute of the OpenAPI schema will be used. When the `name` is not a valid
131+
Python identifier (e.g. begins with a number) this string will be prepended. Defaults to "field\_".
119132

120133
Example:
121134

122135
```yaml
123136
field_prefix: attr_
124137
```
125138

139+
### package_version_override
140+
141+
Specify the package version of the generated client. If unset, the client will use the version of the OpenAPI spec.
142+
143+
Example:
144+
145+
```yaml
146+
package_version_override: 1.2.3
147+
```
148+
126149
[changelog.md]: CHANGELOG.md
127150
[poetry]: https://python-poetry.org/

SECURITY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Only the latest release is currently supported, we will not be backporting fixes
66

77
## Reporting a Vulnerability
88

9-
If you've discovered a vulnerability in this project, please report it to Dylan Anthony at danthony@triaxtec.com. I will create an advisory and add you
9+
If you've discovered a vulnerability in this project, please report it to Dylan Anthony at danthony@triaxtec.com. I will create an advisory and add you
1010
to the discussion / credit you with discovery.
1111

1212
It's better not to create an issue in the repository unless it's already actively being exploited.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
__pycache__/
2+
build/
3+
dist/
4+
*.egg-info/
5+
.pytest_cache/
6+
7+
# pyenv
8+
.python-version
9+
10+
# Environments
11+
.env
12+
.venv
13+
14+
# mypy
15+
.mypy_cache/
16+
.dmypy.json
17+
dmypy.json
18+
19+
# JetBrains
20+
.idea/
21+
22+
/coverage.xml
23+
/.coverage
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# my-test-api-client
2+
A client library for accessing My Test API
3+
4+
## Usage
5+
First, create a client:
6+
7+
```python
8+
from my_test_api_client import Client
9+
10+
client = Client(base_url="https://api.example.com")
11+
```
12+
13+
If the endpoints you're going to hit require authentication, use `AuthenticatedClient` instead:
14+
15+
```python
16+
from my_test_api_client import AuthenticatedClient
17+
18+
client = AuthenticatedClient(base_url="https://api.example.com", token="SuperSecretToken")
19+
```
20+
21+
Now call your endpoint and use your models:
22+
23+
```python
24+
from my_test_api_client.models import MyDataModel
25+
from my_test_api_client.api.my_tag import get_my_data_model
26+
27+
my_data: MyDataModel = get_my_data_model(client=client)
28+
```
29+
30+
Or do the same thing with an async version:
31+
32+
```python
33+
from my_test_api_client.models import MyDataModel
34+
from my_test_api_client.async_api.my_tag import get_my_data_model
35+
36+
my_data: MyDataModel = await get_my_data_model(client=client)
37+
```
38+
39+
Things to know:
40+
1. Every path/method combo becomes a Python function with type annotations.
41+
1. All path/query params, and bodies become method arguments.
42+
1. If your endpoint had any tags on it, the first tag will be used as a module name for the function (my_tag above)
43+
1. Any endpoint which did not have a tag will be in `my_test_api_client.api.default`
44+
1. If the API returns a response code that was not declared in the OpenAPI document, a
45+
`my_test_api_client.api.errors.ApiResponseError` wil be raised
46+
with the `response` attribute set to the `httpx.Response` that was received.
47+
48+
49+
## Building / publishing this Client
50+
This project uses [Poetry](https://python-poetry.org/) to manage dependencies and packaging. Here are the basics:
51+
1. Update the metadata in pyproject.toml (e.g. authors, version)
52+
1. If you're using a private repository, configure it with Poetry
53+
1. `poetry config repositories.<your-repository-name> <url-to-your-repository>`
54+
1. `poetry config http-basic.<your-repository-name> <username> <password>`
55+
1. Publish the client with `poetry publish --build -r <your-repository-name>` or, if for public PyPI, just `poetry publish --build`
56+
57+
If you want to install this client into another project without publishing it (e.g. for development) then:
58+
1. If that project **is using Poetry**, you can simply do `poetry add <path-to-this-client>` from that project
59+
1. If that project is not using Poetry:
60+
1. Build a wheel with `poetry build -f wheel`
61+
1. Install that wheel from the other project `pip install <path-to-wheel>`
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
""" A client library for accessing My Test API """
2+
from .client import AuthenticatedClient, Client
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
""" Contains methods for accessing the API """

end_to_end_tests/golden-record-custom/my_test_api_client/api/default/__init__.py

Whitespace-only changes.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from typing import Optional
2+
3+
import httpx
4+
5+
Client = httpx.Client
6+
7+
8+
def _parse_response(*, response: httpx.Response) -> Optional[bool]:
9+
if response.status_code == 200:
10+
return bool(response.text)
11+
return None
12+
13+
14+
def _build_response(*, response: httpx.Response) -> httpx.Response[bool]:
15+
return httpx.Response(
16+
status_code=response.status_code,
17+
content=response.content,
18+
headers=response.headers,
19+
parsed=_parse_response(response=response),
20+
)
21+
22+
23+
def httpx_request(
24+
*,
25+
client: Client,
26+
) -> httpx.Response[bool]:
27+
28+
response = client.request(
29+
"get",
30+
"/ping",
31+
)
32+
33+
return _build_response(response=response)

end_to_end_tests/golden-record-custom/my_test_api_client/api/tests/__init__.py

Whitespace-only changes.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
from typing import Optional
2+
3+
import httpx
4+
5+
Client = httpx.Client
6+
7+
import datetime
8+
from typing import Dict, List, Optional, Union, cast
9+
10+
from dateutil.parser import isoparse
11+
12+
from ...models.an_enum import AnEnum
13+
from ...models.http_validation_error import HTTPValidationError
14+
15+
16+
def _parse_response(*, response: httpx.Response) -> Optional[Union[None, HTTPValidationError]]:
17+
if response.status_code == 200:
18+
return None
19+
if response.status_code == 422:
20+
return HTTPValidationError.from_dict(cast(Dict[str, Any], response.json()))
21+
return None
22+
23+
24+
def _build_response(*, response: httpx.Response) -> httpx.Response[Union[None, HTTPValidationError]]:
25+
return httpx.Response(
26+
status_code=response.status_code,
27+
content=response.content,
28+
headers=response.headers,
29+
parsed=_parse_response(response=response),
30+
)
31+
32+
33+
def httpx_request(
34+
*,
35+
client: Client,
36+
json_body: Dict[Any, Any],
37+
string_prop: Optional[str] = "the default string",
38+
datetime_prop: Optional[datetime.datetime] = isoparse("1010-10-10T00:00:00"),
39+
date_prop: Optional[datetime.date] = isoparse("1010-10-10").date(),
40+
float_prop: Optional[float] = 3.14,
41+
int_prop: Optional[int] = 7,
42+
boolean_prop: Optional[bool] = False,
43+
list_prop: Optional[List[AnEnum]] = None,
44+
union_prop: Optional[Union[Optional[float], Optional[str]]] = "not a float",
45+
enum_prop: Optional[AnEnum] = None,
46+
) -> httpx.Response[Union[None, HTTPValidationError]]:
47+
48+
json_datetime_prop = datetime_prop.isoformat() if datetime_prop else None
49+
50+
json_date_prop = date_prop.isoformat() if date_prop else None
51+
52+
if list_prop is None:
53+
json_list_prop = None
54+
else:
55+
json_list_prop = []
56+
for list_prop_item_data in list_prop:
57+
list_prop_item = list_prop_item_data.value
58+
59+
json_list_prop.append(list_prop_item)
60+
61+
if union_prop is None:
62+
json_union_prop: Optional[Union[Optional[float], Optional[str]]] = None
63+
elif isinstance(union_prop, float):
64+
json_union_prop = union_prop
65+
else:
66+
json_union_prop = union_prop
67+
68+
json_enum_prop = enum_prop.value if enum_prop else None
69+
70+
params: Dict[str, Any] = {}
71+
if string_prop is not None:
72+
params["string_prop"] = string_prop
73+
if datetime_prop is not None:
74+
params["datetime_prop"] = json_datetime_prop
75+
if date_prop is not None:
76+
params["date_prop"] = json_date_prop
77+
if float_prop is not None:
78+
params["float_prop"] = float_prop
79+
if int_prop is not None:
80+
params["int_prop"] = int_prop
81+
if boolean_prop is not None:
82+
params["boolean_prop"] = boolean_prop
83+
if list_prop is not None:
84+
params["list_prop"] = json_list_prop
85+
if union_prop is not None:
86+
params["union_prop"] = json_union_prop
87+
if enum_prop is not None:
88+
params["enum_prop"] = json_enum_prop
89+
90+
json_json_body = json_body
91+
92+
response = client.request(
93+
"post",
94+
"/tests/defaults",
95+
json=json_json_body,
96+
params=params,
97+
)
98+
99+
return _build_response(response=response)

0 commit comments

Comments
 (0)