Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Tiferet OpenAPI — A Shared OpenAPI Abstraction Layer for the Tiferet Framework

Introduction

Tiferet OpenAPI provides the shared abstraction layer that both tiferet-flask and tiferet-fast depend on for OpenAPI-style API development. It extracts the common domain objects, service interfaces, domain events, mappers, YAML-backed repository, and context classes that were previously duplicated across both framework adapters.

By unifying these components into a single package, tiferet-openapi eliminates code duplication, ensures behavioral consistency between Flask and FastAPI adapters, and provides a clean foundation for building new framework adapters.

Installation

From PyPI

pip install tiferet-openapi

For Development

git clone https://github.com/greatstrength/tiferet-openapi.git
cd tiferet-openapi
python3.10 -m venv .venv
source .venv/bin/activate
pip install -e ".[test]"

Architecture

Tiferet OpenAPI follows the Tiferet framework's layered Domain-Driven Design architecture:

tiferet_openapi/
├── __init__.py          — Version and public exports
├── domain/              — ApiRoute, ApiRouter (DomainObject, Pydantic v2)
├── interfaces/          — OpenApiService (Service ABC)
├── events/              — GetRouters, GetRoute, GetStatusCode (DomainEvent)
├── mappers/             — Aggregates and TransferObjects for YAML round-trip
├── repos/               — OpenApiYamlRepository (YamlLoader-backed OpenApiService)
├── contexts/            — OpenApiSessionContext (AppSessionContext), OpenApiRequestContext
└── blueprints/          — build_openapi_session_context, create_openapi_request_context

Domain Objects

ApiRoute and ApiRouter are read-only Pydantic v2 domain models that represent API routing configuration:

  • ApiRoute — An individual route with id, endpoint (format: router_name.route_id), path, methods, and status_code.
  • ApiRouter — A named group of routes with an optional URL prefix.

Service Interface

OpenApiService is the abstract contract for API configuration access:

  • get_routers() — Retrieve all configured routers.
  • get_route(route_id, router_name=None) — Look up a single route.
  • get_status_code(error_code) — Map an error code to an HTTP status code.

Domain Events

Three domain events encapsulate the service operations for use in the feature workflow:

  • GetRouters — Retrieves all routers via the injected OpenApiService.
  • GetRoute — Parses a dotted endpoint string (e.g., calc.add) and retrieves the matching route.
  • GetStatusCode — Looks up the HTTP status code for a given error code.

Mappers

Aggregates and TransferObjects bridge YAML configuration and runtime domain objects:

  • ApiRouteAggregate, ApiRouterAggregate — Mutable aggregates with route management methods.
  • ApiRouteYamlObject, ApiRouterYamlObject — YAML serialization with _ROLES-based role control, map() for aggregate construction, from_model() for reverse mapping.

Repository

OpenApiYamlRepository is the YAML-backed implementation of OpenApiService. It accepts a parameterized root_key (defaults to "openapi") enabling compatibility with multiple YAML formats:

  • root_key="openapi" — unified openapi.yml format
  • root_key="flask" — legacy flask.yml format
  • root_key="fast" — legacy fast.yml format

Contexts

  • OpenApiSessionContext(AppSessionContext) — Shared API session context that extends the Tiferet application session hub with DomainEvent instances for route and status code lookup. Overrides build_response (attaching an HTTP status code via the route lookup) and handle_error (attaching a resolved status code to a raised TiferetAPIError). Also carries generate_spec and create_docs_handler.
  • OpenApiRequestContext(RequestContext) — Pydantic-aware request context that serializes BaseModel results via model_dump(), with support for lists, dicts, None, and primitives.

Blueprints

  • build_openapi_session_context(app_session, cache, get_route_evt, get_status_code_evt, get_routers_evt, create_request_handler=None, **extra_kwargs) — Composition helper, parallel to the framework's build_cli_session_context, that wires a fully constructed OpenApiSessionContext. Defaults create_request_handler to the framework's plain create_request_context.
  • create_openapi_request_context(interface_id, feature_id, headers=None, data=None) — Request-handler factory that constructs an OpenApiRequestContext instead of a base RequestContext, opting into Pydantic BaseModel serialization. Pass this as create_request_handler to build_openapi_session_context when that behavior is wanted.

YAML Configuration Format

The repository reads configuration from a YAML file with the following structure:

openapi:  # root_key (can be 'flask', 'fast', or any custom key)
  routers:
    calc:
      prefix: /calc
      routes:
        add:
          path: /add
          methods:
            - POST
          status_code: 200
        subtract:
          path: /subtract
          methods:
            - POST
          status_code: 200
    health:
      routes:
        ping:
          path: /ping
          methods:
            - GET
          status_code: 200
  errors:
    INVALID_INPUT: 400
    DIVISION_BY_ZERO: 422
    NOT_FOUND: 404

Usage

Tiferet OpenAPI is consumed by framework-specific adapters. Here's how the shared components integrate:

In tiferet-flask / tiferet-fast

Framework adapters extend OpenApiSessionContext and use OpenApiYamlRepository as their configuration backend:

# Framework adapter context (e.g., FlaskApiContext)
from tiferet_openapi import OpenApiSessionContext, OpenApiRequestContext

class FlaskApiContext(OpenApiSessionContext):
    # Inherits build_request, handle_error, build_response
    # Adds Flask-specific builder logic
    pass

Direct Repository Usage

from tiferet_openapi import OpenApiYamlRepository

# Load configuration
repo = OpenApiYamlRepository(
    openapi_yaml_file='app/configs/openapi.yml',
    root_key='openapi',
)

# Retrieve all routers
routers = repo.get_routers()
for router in routers:
    print(f"{router.name}: {router.prefix}")
    for route in router.routes:
        print(f"  {route.endpoint} -> {route.path} [{', '.join(route.methods)}]")

# Look up a specific route
route = repo.get_route('add', router_name='calc')
print(f"Route: {route.endpoint}, Status: {route.status_code}")

# Map error code to HTTP status
status = repo.get_status_code('INVALID_INPUT')  # Returns 400
status = repo.get_status_code('UNKNOWN')         # Returns 500 (default)

Testing

Run the test suite:

pytest tiferet_openapi/ -v

Tests are co-located in <package>/tests/ directories:

  • Domain/mapper tests use direct Pydantic constructors.
  • Event tests use DomainEvent.handle() with mocked OpenApiService.
  • Repo tests are integration tests using tmp_path with real YAML files.
  • Context tests use mock.Mock(spec=DomainEvent) for event dependencies.

License

MIT — see LICENSE for details.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tiferet_openapi-1.0.0a4.tar.gz (16.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tiferet_openapi-1.0.0a4-py3-none-any.whl (19.2 kB view details)

Uploaded Python 3

File details

Details for the file tiferet_openapi-1.0.0a4.tar.gz.

File metadata

  • Download URL: tiferet_openapi-1.0.0a4.tar.gz
  • Upload date:
  • Size: 16.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tiferet_openapi-1.0.0a4.tar.gz
Algorithm Hash digest
SHA256 4482f5715ace80cfa16265565756909d07007b74d084b816cf87ff6e519e6800
MD5 2bb9dfb33c9fe242692810dfdb06e48e
BLAKE2b-256 02862f851ad329201df4f4a9899f9af6a6ee856f6d53e373b8b6aefc4594faf5

See more details on using hashes here.

Provenance

The following attestation bundles were made for tiferet_openapi-1.0.0a4.tar.gz:

Publisher: python-publish.yml on greatstrength/tiferet-openapi

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tiferet_openapi-1.0.0a4-py3-none-any.whl.

File metadata

File hashes

Hashes for tiferet_openapi-1.0.0a4-py3-none-any.whl
Algorithm Hash digest
SHA256 5850b0cdf1e1a7ce8ae2090ebae6559706ee1f6540ce167fa01c759ba64b29ab
MD5 779e2bb91d213f2533a9ae6680fb672c
BLAKE2b-256 11e7ebd53a3aea1fdc9269dda8d79e909538ff74b48b36039450c194db0c29bd

See more details on using hashes here.

Provenance

The following attestation bundles were made for tiferet_openapi-1.0.0a4-py3-none-any.whl:

Publisher: python-publish.yml on greatstrength/tiferet-openapi

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.0.0a4 This release

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page