Skip to content

Commit

Permalink
0.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Hallett committed Jul 25, 2023
1 parent f51f89b commit 89d4584
Show file tree
Hide file tree
Showing 23 changed files with 881 additions and 150 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ env.ini
__pycache__
.DS_Store
test_output/
dist/
dist/
site/
23 changes: 16 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
# Changelog
# Change log

## 0.1.0
## 0.3.0

- Initial version
- Mostly works with a simple FastAPI generated spec (3.0.2)
- Works with Twilio's spec (see example_openapi_specs/ directory) (3.0.1)
- Almost works with stripes
- Now generates a `MANIFEST` file with information about the build versions
- Added a `constants.py` file to the output if one does not exist yet, which can be used to store values that you do not want to change between subsequent re-generations of the clientele client, such as the API base url.
- Authentication patterns now use `constants.py` for constants values.
- Removed `ipython` from package dependencies and moved to dev dependencies.
- Documentation! [https://beckett-software.github.io/clientele/](https://beckett-software.github.io/clientele/)

### 0.2.0
## 0.2.0

- Improved CLI output
- Code organisation is now sensible and not just one giant file
- Now supports an openapi spec generated from a dotnet project (`Microsoft.OpenApi.Models`)
- async client support fully working
- HTTP Bearer support
- HTTP Basic support


## 0.1.0

- Initial version
- Mostly works with a simple FastAPI generated spec (3.0.2)
- Works with Twilio's spec (see example_openapi_specs/ directory) (3.0.1)
- Almost works with stripes
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
help:
@echo Developer commands for PopenAPI
@echo Developer commands for Clientele
@echo
@grep -E '^[ .a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
@echo
Expand All @@ -19,6 +19,8 @@ clean: ## Clear any cache files and test files
rm -rf .pytest_cache
rm -rf .ruff_cache
rm -rf test_output
rm -rf site/
rm -rf dist/

shell: ## Run an ipython shell
poetry run ipython
46 changes: 10 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
# ⚜️ Clientele
# ⚜️ Clientele

### Typed API Clients from OpenAPI specs
# Typed API Clients from OpenAPI specs

![clientele_logo](https://github.com/beckett-software/clientele/blob/main/docs/clientele.jpeg?raw=true)

Clientele lets you generate fully-typed, functional, API Clients from OpenAPI specs.

It uses modern tools to be blazing fast and type safe.
It uses modern tools to be blazing fast and type safe.

Plus - there is no complex boilerplate and the generated code is very small.

## Features

* Fully typed API Client using Pydantic.
* Minimalist and easy to use - the generated code is tiny.
* Choose either sync (default) or async - we support both.
* Generates authentication code for you (curently only supports HTTP Bearer auth)
* Minimalist and easy to use - the generated code is designed for readability.
* Choose either sync or async - we support both, and you can switch between them easily.
* Supports authentication (curently only HTTP Bearer and HTTP Basic auth).
* Written entirely in Python - no need to install other languages to use OpenAPI.
* The client footprint is minimal - it only requires `httpx` and `pydantic`.
* Supports your own configuration - we provide an entry point that will never be overwritten.

We're built on:

Expand All @@ -32,36 +34,8 @@ poetry add clientele

## Usage

### From URLs

```sh
clientele generate -u http://URL_TO_OPEN_API.json -o output/
```

### From files

```sh
clientele generate -f path/to/file.json -o output/
clientele generate -f path/to/file.json -o my_client/ --asyncio t
```

### Async Client

```sh
clientele generate -f path/to/file.json -o output/ --asyncio t
```

## Authentication

If your OpenAPI spec provides security information for the following authentication methods:

* HTTP Bearer

Then clientele will provide you information on the environment variables you need to set to
make this work during the generation. For example:

```sh
Please set
* MY_CLIENT_AUTH_USER_KEY
* MY_CLIENT_AUTH_PASS_KEY
environment variable to use basic authentication
```
[Read the docs](https://beckett-software.github.io/clientele/)
26 changes: 26 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Change log

## 0.3.0

- Now generates a `MANIFEST` file with information about the build versions
- Added a `constants.py` file to the output if one does not exist yet, which can be used to store values that you do not want to change between subsequent re-generations of the clientele client, such as the API base url.
- Authentication patterns now use `constants.py` for constants values.
- Removed `ipython` from package dependencies and moved to dev dependencies.
- Documentation! [https://beckett-software.github.io/clientele/](https://beckett-software.github.io/clientele/)

## 0.2.0

- Improved CLI output
- Code organisation is now sensible and not just one giant file
- Now supports an openapi spec generated from a dotnet project (`Microsoft.OpenApi.Models`)
- async client support fully working
- HTTP Bearer support
- HTTP Basic support


## 0.1.0

- Initial version
- Mostly works with a simple FastAPI generated spec (3.0.2)
- Works with Twilio's spec (see example_openapi_specs/ directory) (3.0.1)
- Almost works with stripes
Binary file removed docs/beckett_api.jpeg
Binary file not shown.
19 changes: 19 additions & 0 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 💱 Compatability

When we were building Clientele, we discovered that, despite a fantastic [specification](https://www.openapis.org/), OpenAPI has a lot of poor implementations.

As pythonistas, we started with the auto-generated OpenAPI schemas provided by [FastAPI](https://fastapi.tiangolo.com/), and then we branched out to large APIs like [Twilio](https://www.twilio.com/docs/openapi) to test what we built.

Despite the effort, we still keep finding subtly different OpenAPI implementations. Because of this we cannot guarentee 100% compatability with an API, but we can give you a good indication of what we've tested.

## Works well with

Any bog-standard `3.0.x` implementation works very well.

* [FastAPI](https://fastapi.tiangolo.com/tutorial/first-steps/?h=openapi#what-is-openapi-for)'s OpenAPI schema is very well supported.
* [Microsoft's OpenAPI spec](https://learn.microsoft.com/en-us/azure/api-management/import-api-from-oas?tabs=portal) has also been battle tested on an internal project, and works well.


## Does not work

We do not support `2.x` aka "Swagger" - this format is quite different and deprecated.
151 changes: 151 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# 🪄 Client example

Let's build an API Client using clientele and an example OpenAPI schema.

Our [GitHub](https://github.com/beckett-software/clientele/tree/main/example_openapi_specs) has a bunch of schemas that are proven to work with clientele, so let's use one of those!

## Generate the client

Very simply:

```sh
clientele generate -u https://raw.githubusercontent.com/beckett-software/clientele/main/example_openapi_specs/simple.json -o my_client/
```

The `-u` parameter expects a URL, you can provide a path to a file with `-f` instead if you download the file.

The `-o` parameter is the output directory of the generated client.

Run it now and you will see this output:

```sh
my_client/
__init__.py
client.py
constants.py
http.py
MANIFEST
schemas.py
```

## Client directory

Let's go over each file and talk about what it does

### client.py

This file provides all the client functions.

```py title="my_client/client.py" linenums="1"
import typing # noqa
from . import schemas # noqa
from . import http # noqa


def HealthCheckHealthCheckGet() -> schemas.HealthCheckResponse:
response = http.get("/health-check")
return http.handle_response(HealthCheckHealthCheckGet, response)


def TestInputTestInputPost(
data: schemas.TestInputData,
) -> typing.Union[schemas.HTTPValidationError, schemas.TestInputResponse]:
response = http.post("/test-input", data=data and data.model_dump())
return http.handle_response(TestInputTestInputPost, response)
```

We can see one of the functions here, `HealthCheckHealthCheckGet`, is for a straight-forward HTTP GET request without any input arguments, and it returns a schema object. If the endpoint has url parameters or query parameters, they will appear as input arguments to the function.

```py
def HealthCheckHealthCheckGet() -> schemas.HealthCheckResponse:
response = http.get("/health-check")
return http.handle_response(HealthCheckHealthCheckGet, response)
```

Here is how you might use it:

```py
from my_client import client

client.HealthCheckHealthCheckGet()
>>> HealthCheckResponse(status='ok')
```

A more complex example is shown just below - `TestInputTestInputPost`, this is for an HTTP POST method, and it requires an input property called `data` that is an instance of a schema, and returns a union of responses. If the endpoint has url parameters or query parameters, they will appear as input arguments to the function alongside the `data` argument.

```py
def TestInputTestInputPost(
data: schemas.TestInputData,
) -> typing.Union[schemas.HTTPValidationError, schemas.TestInputResponse]:
response = http.post("/test-input", data=data and data.model_dump())
return http.handle_response(TestInputTestInputPost, response)
```

Here is how you might use it:

```py
from my_client import client, schemas

data = schemas.TestInputData(my_title="Hello, world")
client.TestInputTestInputPost(data=data)
>>> TestInputResponse(title='Hello, world')
```

Because we're using Pydantic to manage the input data, we get a strongly-typed interface for us.

### schemas.py

This file has all the possible schemas, request and response, for the API.

They are all subclassed from pydantic's `BaseModel`. Here are a few examples:

```py title="my_client/schemas.py" linenums="1"
import typing # noqa
from pydantic import BaseModel # noqa
from enum import Enum # noqa


class HTTPValidationError(BaseModel):
detail: typing.List[typing.Any]


class HealthCheckResponse(BaseModel):
status: str


class TestInputData(BaseModel):
my_title: str


class TestInputResponse(BaseModel):
title: str


class ValidationError(BaseModel):
loc: typing.List[typing.Any]
msg: str
type: str

```

### http.py

This file manages the HTTP layer of the client.

You should never need to touch the code here, but feel free to have a browse.

This file will have either a sync, or an async instance of a client from `httpx`.

It also manages the coersion of response bodies into schema objects.

### constants.py

This manages all the configuration your client might need.

This file is only ever generated once, and will never be overwritten (unless you delete it yourself).

You can modify the contents of the functions in this file to provide things like the base url and authentication keys for your client.

### MANIFEST

The `MANIFEST` file provides some information on how the client was generated.
27 changes: 27 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# ⚜️ Clientele

# Typed API Clients from OpenAPI specs

![clientele_logo](https://github.com/beckett-software/clientele/blob/main/docs/clientele.jpeg?raw=true)

Clientele lets you generate fully-typed, functional, API Clients from OpenAPI specs.

It uses modern tools to be blazing fast and type safe.

Plus - there is no complex boilerplate and the generated code is very small.

## Features

* Fully typed API Client using Pydantic.
* Minimalist and easy to use - the generated code is designed for readability.
* Choose either sync or async - we support both, and you can switch between them easily.
* Supports authentication (curently only HTTP Bearer and HTTP Basic auth).
* Written entirely in Python - no need to install other languages to use OpenAPI.
* The client footprint is minimal - it only requires `httpx` and `pydantic`.
* Supports your own configuration - we provide an entry point that will never be overwritten.

We're built on:

* [Pydantic 2.0](https://docs.pydantic.dev/latest/)
* [httpx](https://www.python-httpx.org/)
* [openapi-core](https://openapi-core.readthedocs.io/en/latest/)
13 changes: 13 additions & 0 deletions docs/install.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 🏗️ Install

We recommend using [poetry](https://python-poetry.org/) to manage your python packages.

```sh
poetry add clientele
```

We also work with [pip](https://pip.pypa.io/en/stable/)

```sh
pip install clientele
```
Loading

0 comments on commit 89d4584

Please sign in to comment.