Implement Phase 9: client libraries (Go, Rust, Lisp, Python)
- clients/README.md: canonical API surface and error type reference - clients/testdata/: shared JSON response fixtures - clients/go/: mciasgoclient package; net/http + TLS 1.2+; sync.RWMutex token state; DisallowUnknownFields on all decoders; 25 tests pass - clients/rust/: async mcias-client crate; reqwest+rustls (no OpenSSL); thiserror MciasError enum; Arc<RwLock> token state; 22+1 tests pass; cargo clippy -D warnings clean - clients/lisp/: ASDF mcias-client; dexador HTTP, yason JSON; mcias-error condition hierarchy; Hunchentoot mock-dispatcher; 37 fiveam checks pass on SBCL 2.6.1; yason boolean normalisation in validate-token - clients/python/: mcias_client package (Python 3.11+); httpx sync; py.typed; dataclasses; 32 pytest tests; mypy --strict + ruff clean - test/mock/mockserver.go: in-memory mock server for Go client tests - ARCHITECTURE.md §19: updated per-language notes to match implementation - PROGRESS.md: Phase 9 marked complete - .gitignore: exclude clients/rust/target/, python .venv, .pytest_cache, .fasl files Security: token never logged or exposed in error messages in any library; TLS enforced in all four languages; token stored under lock/mutex/RwLock
This commit is contained in:
@@ -1055,44 +1055,55 @@ Error types exposed by every library:
|
||||
|
||||
#### Common Lisp (`clients/lisp/`)
|
||||
|
||||
- ASDF system: `mcias-client`
|
||||
- HTTP: `dexador`
|
||||
- JSON: `yason` (or `cl-json`; prefer `yason` for streaming)
|
||||
- TLS: delegated to Dexador/Usocket; custom CA documented per platform
|
||||
- API: CLOS class `mcias-client` with slot `token`; methods are generic
|
||||
functions
|
||||
- Conditions: `mcias-error`, `mcias-unauthenticated`, `mcias-forbidden`,
|
||||
`mcias-not-found` — all subclasses of `mcias-error`
|
||||
- Tests: `fiveam` test suite; mock responses via local TCP server or
|
||||
Dexador's mock facility if available
|
||||
- Compatibility: SBCL primary; CCL secondary
|
||||
- ASDF system: `mcias-client` (quickload-able via Quicklisp)
|
||||
- HTTP: `dexador` (synchronous)
|
||||
- JSON: `yason` for both encoding and decoding; all booleans normalised
|
||||
(yason returns `:false` for JSON `false`; client coerces to `nil`)
|
||||
- TLS: delegated to Dexador/Usocket/cl+ssl; custom CA documented per platform
|
||||
- API: CLOS class `mcias-client` with `client-base-url` reader and
|
||||
`client-token` accessor; plain functions (not generic) for all operations
|
||||
- Conditions: `mcias-error` base with subclasses `mcias-auth-error`,
|
||||
`mcias-forbidden-error`, `mcias-not-found-error`, `mcias-input-error`,
|
||||
`mcias-conflict-error`, `mcias-server-error`
|
||||
- Tests: 37 checks in `fiveam`; mock server implemented with Hunchentoot
|
||||
(`mock-dispatcher` subclass overriding `handle-request`); all fiveam
|
||||
symbols explicitly prefixed to avoid SBCL package-lock violations
|
||||
- Compatibility: SBCL 2.x primary
|
||||
|
||||
#### Python (`clients/python/`)
|
||||
|
||||
- Package: `mcias_client` (PEP 517 build; `pyproject.toml`)
|
||||
- HTTP: `httpx` (provides both sync `MciasClient` and async `AsyncMciasClient`)
|
||||
- TLS: `ssl.create_default_context(cafile=...)` for custom CA
|
||||
- Package: `mcias_client` (PEP 517 build; `pyproject.toml` / setuptools)
|
||||
- HTTP: `httpx` sync client; `Client` is a context manager (`__enter__`/`__exit__`)
|
||||
- TLS: `ssl.create_default_context(cafile=...)` for custom CA cert
|
||||
- Types: `py.typed` marker; all public symbols fully annotated; `mypy --strict`
|
||||
- Errors: `MciasError(Exception)` base with subclasses as listed above
|
||||
- Token state: `_token: str | None` instance attribute; thread-safe in sync
|
||||
variant via `threading.Lock`
|
||||
- Python version support: 3.11+
|
||||
- Linting: `ruff check` (replaces flake8/isort); `ruff format` for style
|
||||
passes with zero issues; dataclasses for `Account`, `PublicKey`, `PGCreds`
|
||||
- Errors: `MciasError(Exception)` base with subclasses as listed above;
|
||||
`raise_for_status()` dispatcher maps status codes to typed exceptions
|
||||
- Token state: `token: str | None` public attribute (single-threaded use assumed)
|
||||
- Python version support: 3.11+ (uses `datetime.UTC`, `X | Y` union syntax)
|
||||
- Linting: `ruff check` (E/F/W/I/UP rules, 88-char line limit); `ruff format`
|
||||
- Tests: 32 pytest tests using `respx` for httpx mocking
|
||||
|
||||
### Versioning Strategy
|
||||
|
||||
Each client library follows the MCIAS server's minor version. Breaking changes
|
||||
to the API surface increment the major version. The proto definitions (Phase 7)
|
||||
serve as the source of truth for the canonical interface; client libraries
|
||||
implement a subset matching the REST API.
|
||||
to the API surface increment the major version. The REST API surface defined in
|
||||
`clients/README.md` serves as the source of truth; client libraries
|
||||
implement the full surface.
|
||||
|
||||
Client libraries are not coupled to each other. A user of the Python library
|
||||
does not need the Go library installed.
|
||||
|
||||
### Mock Server
|
||||
### Mock Servers
|
||||
|
||||
`test/mock/` contains a Go binary (`mcias-mock`) that implements a minimal
|
||||
in-memory MCIAS server for use in client library integration tests. It
|
||||
supports the full REST API surface with configurable fixture responses.
|
||||
Language-specific test suites start `mcias-mock` as a subprocess and connect
|
||||
to it over localhost TLS (using a bundled self-signed test certificate).
|
||||
`test/mock/mockserver.go` provides a Go `httptest.Server`-compatible mock
|
||||
MCIAS server (struct `Server`) for use in Go client integration tests. It
|
||||
maintains in-memory account/token/revocation state with `sync.RWMutex`.
|
||||
|
||||
Each other language library includes its own inline mock:
|
||||
|
||||
- **Rust**: `wiremock::MockServer` with per-test `Mock` stubs
|
||||
- **Common Lisp**: Hunchentoot acceptor (`mock-dispatcher`) in
|
||||
`tests/mock-server.lisp`; started on a random port per test via
|
||||
`start-mock-server` / `stop-mock-server`
|
||||
- **Python**: `respx` mock transport for `httpx`; `@respx.mock` decorator
|
||||
|
||||
Reference in New Issue
Block a user