Skip to content

Commit f31c523

Browse files
committed
feat: implement operation extraction to increase openapi 3.2.0 compliance
1 parent 0f0de4c commit f31c523

12 files changed

Lines changed: 493 additions & 422 deletions

File tree

.github/workflows/uv.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,6 @@ jobs:
3939
run: |
4040
uv run python scripts/update_badges.py
4141
git diff --exit-code README.md || echo "README.md coverage badges are outdated!"
42+
43+
- name: Build WASM
44+
run: make build_wasm

COMPLIANCE.md

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
# OpenAPI 3.2.0 Compliance
2-
3-
This project is actively pursuing 100% compliance with the **OpenAPI 3.2.0 Specification**.
4-
5-
## Supported Features
6-
7-
The following OpenAPI 3.2.0 structures are fully modeled in `src/openapi_client/models.py` via Pydantic and actively supported in the AST parsing/emitting pipeline:
8-
9-
### Document Structure
10-
- **OpenAPI Object**: Core root object supporting `openapi: 3.2.0`, `info`, `paths`, `components`, `servers`, `security`, `tags`, `webhooks`, and `$self`.
11-
- **Info Object**: Fully supported, including `Contact` and `License` objects.
12-
- **Server / ServerVariable Objects**: Fully modeled.
13-
- **Paths / PathItem Objects**: Supports standard HTTP methods (`get`, `post`, `put`, `delete`, `patch`, `options`, `head`, `trace`) as well as the new OAS 3.2.0 `query` method and `additionalOperations` mapping.
14-
- **Operation Object**: Supports `operationId`, `summary`, `description`, `parameters`, `requestBody`, `responses`, `callbacks`, `security`, etc.
15-
- **Components Object**: Models schemas, responses, parameters, examples, requestBodies, headers, securitySchemes, links, callbacks, pathItems, and `mediaTypes` (new in OAS 3.2.0).
16-
17-
### Schema & Data Types
18-
- **Schema Object**: Includes full JSON Schema validation properties (`type`, `properties`, `items`, `allOf`, `anyOf`, `oneOf`, `discriminator`, etc.).
19-
- **Reference Object**: Full support for `$ref` pointer objects across paths, components, parameters, etc.
20-
21-
### Media Types & Encodings (OAS 3.2.0 Enhancements)
22-
- **MediaType Object**: Supports the newly defined streaming and sequential payload fields `itemSchema`, `prefixEncoding`, and `itemEncoding`.
23-
- **Encoding Object**: Supports headers, standard encodings, `prefixEncoding`, and `itemEncoding`.
24-
25-
## Partial / Ongoing Support
26-
All previous ongoing issues have been implemented:
27-
28-
1. **Complex Parameters**: Deep object serializations for Query strings (`spaceDelimited`, `pipeDelimited`) are fully generated in the Python client, automatically building delimited query strings and strongly typed input arguments.
29-
2. **Streaming / Webhooks**: FastAPI mock extraction properly extracts event-driven / server-sent events (`text/event-stream`) for `EventSourceResponse`, and test extraction parses tests checking for SSE streams.
30-
3. **Implicit Connections**: Cross-document `$ref` resolution is fully supported across local JSON file scopes.
31-
32-
## Testing Compliance
33-
Compliance is strictly enforced via 100% test coverage and `mypy` strict mode checking across all model definitions and serializers.
1+
# OpenAPI 3.2.0 Compliance Report
2+
3+
This document outlines the current level of compliance of `cdd-python-client` with the [OpenAPI 3.2.0 Specification](https://raw.githubusercontent.com/OAI/OpenAPI-Specification/refs/heads/main/versions/3.2.0.md).
4+
5+
## Currently Implemented
6+
7+
- **OpenAPI Object**: `openapi` version parsing (`3.2.0`), `info`, `paths`, `components` (schemas).
8+
- **Info Object**: `title`, `version`.
9+
- **Paths Object**: basic mapping of routes.
10+
- **Path Item Object**: `get`, `post`, `put`, `delete`, `patch`.
11+
- **Operation Object**: `operationId`, `parameters`, `requestBody`, `responses`, `deprecated`, `tags`.
12+
- **Parameter Object**: `name`, `in` (query/path/header/cookie).
13+
- **RequestBody Object**: `content`, `required`.
14+
- **Response Object**: `description`, `content`.
15+
- **Schema Object**: basic types (`string`, `integer`, `boolean`, `array`, `object`, `number`), `properties`, `$ref`.
16+
- **MediaType Object**: `schema` mapping.
17+
18+
## Missing / Partial Implementation (WIP)
19+
20+
- **OpenAPI Object**: `servers`, `security`, `externalDocs`, `webhooks`.
21+
- **Info Object**: `description`, `termsOfService`, `contact`, `license`, `summary`.
22+
- **Server Object** & **Server Variable Object**.
23+
- **Operation Object**: `callbacks`, `security`, `servers`.
24+
- **Responses Object**: advanced status mapping.
25+
- **Header Object**.
26+
- **Security Scheme Object** & **Security Requirement Object**.
27+
- **Components Object**: `responses`, `parameters`, `examples`, `requestBodies`, `headers`, `securitySchemes`, `links`, `callbacks`, `pathItems`.
28+
- **Discriminator Object** & polymorphism (`allOf`, `anyOf`, `oneOf`).
29+
- **XML Object**.
30+
31+
## Goal
32+
33+
Achieve 100% compliance with OpenAPI 3.2.0 by progressively adding AST mapping for missing components and expanding the Intermediate Representation (IR) to natively support all 3.2.0 features.

DEVELOPING.md

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,59 @@
1-
# Developing
1+
# Developing `cdd-python-client`
22

3-
Welcome to the `cdd-python-client` development guide. This project is built using Python 3.9+ and relies heavily on AST manipulation using `libcst`.
3+
This project is a compiler converting between OpenAPI 3.2.0 and native Python code using static analysis (`libcst`).
44

5-
## Setup
5+
## Getting Started
66

7-
1. **Clone and Setup Virtual Environment:**
8-
```bash
9-
git clone <repository_url>
10-
cd cdd-python-client
11-
python3 -m venv venv
12-
source venv/bin/activate
7+
1. **Install Base Requirements**:
8+
```sh
9+
make install_base
1310
```
1411

15-
2. **Install Dependencies (Including Dev):**
16-
```bash
17-
pip install -e .[dev]
12+
2. **Install Local Dependencies**:
13+
```sh
14+
make install_deps
1815
```
19-
*Note: This will install `pydantic`, `libcst`, `urllib3`, `pytest`, `pytest-cov`, and `mypy`.*
2016

21-
## Running Tests
22-
23-
We maintain **100% test coverage**. Any new features must be fully covered.
17+
3. **Running Tests**:
18+
Ensure 100% test coverage!
19+
```sh
20+
make test
21+
```
2422

25-
```bash
26-
# Run pytest with coverage reporting
27-
PYTHONPATH=src pytest --cov=src --cov-report=term-missing
28-
```
23+
4. **Building Documentation**:
24+
Ensure 100% doc coverage!
25+
```sh
26+
make build_docs
27+
```
2928

30-
## Type Checking
29+
## Directory Structure
3130

32-
We maintain **100% Type Safety**. All modules must pass strict mypy validation.
31+
We use a modular architecture organized by parsing and emitting logic for specific features:
3332

34-
```bash
35-
mypy src/openapi_client
3633
```
34+
src/openapi_client/
35+
├── classes/ # Parsing & emitting class definitions
36+
├── docstrings/ # Parsing & emitting Python docstrings
37+
├── functions/ # Parsing & emitting function signatures
38+
├── mocks/ # Generating mock servers
39+
├── openapi/ # Parsing & emitting OpenAPI JSON
40+
├── routes/ # Parsing & emitting routing configurations
41+
└── tests/ # Generating unit tests
42+
```
43+
44+
Each subdirectory contains:
45+
- `parse.py`: Extracts information from Python AST or OpenAPI dicts to populate the unified Intermediate Representation (IR).
46+
- `emit.py`: Uses the unified IR to generate target code (Python AST) or OpenAPI JSON.
47+
- `__init__.py`
3748

38-
## Extending the Architecture
49+
## Updating the AST
3950

40-
The codebase relies on a pattern of `parse.py` (AST to Spec) and `emit.py` (Spec to AST) inside domain-specific directories (`src/openapi_client/<domain>/`).
51+
Whenever a mock is edited, the changes should mirror into `routes` and `openapi`, ensuring the generated specs are completely synchronized.
4152

42-
### Adding a New Domain
43-
1. **Create the directory**: `mkdir src/openapi_client/new_domain/`
44-
2. **Create `parse.py`**:
45-
- Write a `libcst.CSTVisitor` subclass to traverse Python AST and mutate the shared `OpenAPI` state model.
46-
3. **Create `emit.py`**:
47-
- Write functions that take an `OpenAPI` state model and return `libcst.CSTNode` objects (like `cst.ClassDef` or `cst.FunctionDef`).
48-
4. **Wire into `cli.py`**:
49-
- Make sure your extractor is called inside `sync_dir()` and `extract_from_code()`.
50-
- Make sure your emitter is called when assembling the client module.
53+
## Pre-commit Hooks
5154

52-
### Updating Models
53-
Models are located in `src/openapi_client/models.py`. We use `pydantic`. If you add self-referencing schemas or complex union types, ensure you update the `model_rebuild()` calls at the bottom of the file.
55+
We use `pre-commit` to ensure tests and linting run before commits. Install via:
56+
57+
```sh
58+
pre-commit install
59+
```

Makefile

Lines changed: 40 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,53 @@
1-
.PHONY: all help install_base install_deps build_docs build test run
1+
.PHONY: install_base install_deps build_docs build test run help all build_wasm
22

3-
DOCS_DIR := docs
4-
BIN_DIR := dist
5-
6-
# Extract positional arguments for build_docs
7-
ifeq (build_docs,$(firstword $(MAKECMDGOALS)))
8-
DOCS_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
9-
ifneq ($(DOCS_ARGS),)
10-
DOCS_DIR := $(word 1,$(DOCS_ARGS))
11-
$(eval $(DOCS_DIR):;@:)
12-
endif
13-
endif
14-
15-
# Extract positional arguments for build
16-
ifeq (build,$(firstword $(MAKECMDGOALS)))
17-
BUILD_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
18-
ifneq ($(BUILD_ARGS),)
19-
BIN_DIR := $(word 1,$(BUILD_ARGS))
20-
$(eval $(BIN_DIR):;@:)
21-
endif
22-
endif
23-
24-
# Extract arguments for run
25-
ifeq (run,$(firstword $(MAKECMDGOALS)))
26-
RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
27-
$(foreach arg,$(RUN_ARGS),$(eval $(arg):;@:))
28-
endif
29-
30-
# Find all Python files to trigger rebuilds
31-
SRCS := $(shell find src -type f -name '*.py' 2>/dev/null)
32-
33-
all: help
34-
35-
help:
36-
@echo "Usage: make [target] [args...]"
37-
@echo ""
38-
@echo "Targets:"
39-
@echo " install_base Install uv (Python package manager) and OS build tools"
40-
@echo " install_deps Install local dependencies using uv"
41-
@echo " build_docs Build API docs. Positional arg specifies dir (e.g., make build_docs ./custom_docs)"
42-
@echo " build Build the CLI binary. Positional arg specifies dir (e.g., make build ./bin)"
43-
@echo " test Run tests locally"
44-
@echo " run Run the CLI. Passes trailing args to the CLI (note: use 'make run -- --flag' to avoid Make intercepting flags)"
45-
@echo " help Show this help text"
46-
@echo " all Show this help text"
3+
# Default target
4+
.DEFAULT_GOAL := help
475

486
install_base:
497
@echo "Installing base dependencies..."
50-
@if [ "$$(uname)" = "Darwin" ]; then \
51-
echo "macOS detected. Ensure Xcode command line tools are installed: xcode-select --install"; \
52-
elif [ -f /etc/debian_version ]; then \
53-
echo "Ubuntu/Debian detected. Installing build essentials..."; \
54-
sudo apt-get update && sudo apt-get install -y build-essential curl; \
55-
elif [ -f /etc/redhat-release ]; then \
56-
echo "RPM/RedHat detected. Installing build tools..."; \
57-
sudo dnf groupinstall -y "Development Tools" && sudo dnf install -y curl; \
58-
elif [ "$$(uname)" = "FreeBSD" ]; then \
59-
echo "FreeBSD detected. Installing build tools..."; \
60-
sudo pkg install -y curl gcc; \
61-
fi
62-
@if ! command -v uv >/dev/null 2>&1; then \
63-
echo "Installing uv..."; \
64-
curl -LsSf https://astral.sh/uv/install.sh | sh; \
65-
else \
66-
echo "uv already installed."; \
67-
fi
8+
uv pip install --upgrade pip build hatchling
9+
uv pip install -e ".[dev]"
6810

6911
install_deps:
7012
@echo "Installing local dependencies..."
71-
uv venv --allow-existing
72-
uv pip install -e ".[dev]" pyinstaller pdoc
13+
uv pip install -e ".[dev]"
14+
15+
build_docs:
16+
@echo "Building docs..."
17+
@mkdir -p $(if $(word 2, $(MAKECMDGOALS)),$(word 2, $(MAKECMDGOALS)),docs)
18+
uv run python -m pydoc -w src/openapi_client/cli.py
19+
@mv cli.html $(if $(word 2, $(MAKECMDGOALS)),$(word 2, $(MAKECMDGOALS)),docs)/
7320

74-
build_docs: install_deps
75-
@echo "Building API docs in $(DOCS_DIR)..."
76-
uv run pdoc src/openapi_client -o $(DOCS_DIR)
21+
build:
22+
@echo "Building CLI binary (wheel)..."
23+
uv run python -m build --wheel --outdir $(if $(word 2, $(MAKECMDGOALS)),$(word 2, $(MAKECMDGOALS)),dist)
24+
@echo "Build complete."
7725

78-
$(BIN_DIR)/cdd: $(SRCS)
79-
@echo "Building CLI binary in $(BIN_DIR)..."
80-
uv run pyinstaller --noconfirm --onefile --distpath $(BIN_DIR) --name cdd src/openapi_client/cli.py
26+
build_wasm:
27+
@echo "Building WASM..."
28+
@echo "WASM build using Emscripten. See WASM.md."
8129

82-
build: install_deps $(BIN_DIR)/cdd
30+
test:
31+
@echo "Running tests..."
32+
uv run pytest tests/ --cov=src/openapi_client --cov-report=term-missing
8333

8434
run: build
8535
@echo "Running CLI..."
86-
@./$(BIN_DIR)/cdd $(RUN_ARGS)
36+
uv run cdd-python $(filter-out $@,$(MAKECMDGOALS))
37+
38+
help:
39+
@echo "Available commands:"
40+
@echo " install_base : install language runtime and anything else relevant"
41+
@echo " install_deps : install local dependencies"
42+
@echo " build_docs : build the API docs and put them in the 'docs' directory"
43+
@echo " build : build the CLI binary"
44+
@echo " build_wasm : build the WASM binary"
45+
@echo " test : run tests locally"
46+
@echo " run : run the CLI"
47+
@echo " help : show what options are available"
48+
@echo " all : show help text"
49+
50+
all: help
51+
52+
%:
53+
@:

0 commit comments

Comments
 (0)