Skip to content

OpenAPI Integration

Arrest additionally offers integration to OpenAPI specification (formerly Swagger).

With it, you can generate the necessary boilerplates for Arrest services and resources directly from the API route specifications. Arrest uses datamodel-code-generator under the hood to generate the data transfer models in Pydantic, and add Arrest resource and service templates on top of that.

Note

this feature is experimental and does not quite cover the extensive range of features of OpenAPI specification. We will be gradually rolling out new features from OpenAPI specification that also suits the functionalities of Arrest.

Usage

pip install "arrest[openapi]"

poetry add 'arrest[openapi]'

This installs two additional dependencies.

  1. datamodel-code-generator
  2. jinja2

Once installed, you can use the CLI interface to provide a OpenAPI Specification url (http or a filepath) and it will generate the files from the spec.

arrest --url https://petstore3.swagger.io/api/v3/openapi.json

This generates 3 files. 1. models.py - a file containing all the schema definitions found in the OpenAPI Spec /components/schemas, generated by datamodel-code-generator

  1. resources.py - a set of Arrest resource definitions based on the defined routes in the Spec /paths

  2. services.py - a list of Arrest services based on the Spec /servers, if there is a server definition with serverVariables, Arrest will generate as many service instance as there are unique server urls from all the combinations of serverVariables

Example

>>> arrest --url https://petstore3.swagger.io/api/v3.1/openapi_3.1.json -o path/to/destination

this will create a directory named swagger_petstore_openapi_3_1 (based on the title in the OpenAPI specification) in path/to/destination, inside which it will generate the following

# swagger_petstore_openapi_3_1/models.py

# generated by datamodel-codegen:
#   filename:  <stdin>
#   timestamp: 2024-01-29T18:57:34+00:00

from __future__ import annotations

from typing import List, Optional

from pydantic import BaseModel


class Pet(BaseModel):
    id: int
    name: str
    tag: Optional[str] = None


class Pets(BaseModel):
    __root__: List[Pet]


class Error(BaseModel):
    code: int
    message: str



# swagger_petstore_openapi_3_1/resources.py

from arrest import Resource
from .models import Pet, Pets

pets = Resource(
    name="pets",
    route="/pets",
    handlers=[
        ("GET", "", None, Pets),
        ("POST", "", None, None),
        ("GET", "/{petId}", None, Pet),
    ]
)


# swagger_petstore_openapi_3_1/services.py

from arrest import Service
from .resources import pet, store, user

swagger_petstore_openapi_3_0 = Service(
    name="swagger_petstore_openapi_3_0",
    url="/api/v3",
    resources=[pet, store, user]
)

The files generated are not black-formatted or isort-formatted. Hence further customization is left to the user.

CLI Arguments

usage:
    arrest [options] --help

generate arrest services and resources from various definitions

options:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                        output directory for generated files (default: current working directory)
  --pydantic {v1,v2}    pydantic version to generate the schema definitions
  -u URL, --url URL     HTTP or file url for the openapi schema
  -d DIR, --dir DIR     Folder containing the files (default: OpenAPI specification title)

By default Arrest will look for the title of the specification and use that to name the directory that contains these files. If it can't find one, it will use api as the directory name instead. Alternatively you can specify your own custom name by providing -d or --dir

--pydantic is used to denote the pydantic version (v1 or v2) that the generated schema definitions will use (defaults to v1)

What works and what does not

  1. Currently, Arrest is able to only work on singular pydantic types and not types like list[BaseModel] or dict[str, BaseModel] as request / response models. As a result, schema definitions in OpenAPI that are of the following types:

    {
        "type": "array",
        "items": {
            "$ref": "#/components/Schemas/User"
        }
    }
    
    are not parsed. Same for any schema definitions that don't have a $ref to a model definition.

  2. Parameters such as path or query or headers are not parsed currently. Although formatted path interpolation is available. If your path definition is something like /abc/{xyz}, the Arrest resource abc will have a handler with path /{xyz} and you can pass xyz as a kwarg when calling this route.

Example

abc = Resource(
    name="abc",
    route="/abc",
    handlers=[
        ("GET", "/{xyz}", None, None),
    ]
)


await my_service.abc.get(xyz=123)
# or
await my_service.abc.get("/123")
  1. OpenAPI Security definitions are not parsed.

There is a bit of manual intervention needed if the extraction from the OpenAPI Spec is incomplete. You can subclass the generated schema classes and add extra parameters as Header() or Query() parameters.

Additionally, you can use it as a module and extend its functionalities.

from arrest.openapi import OpenAPIGenerator

...

For more info, check the API Documentation