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:
2026-03-11 16:38:32 -07:00
parent f34e9a69a0
commit 0c441f5c4f
1974 changed files with 10151 additions and 33 deletions

View File

@@ -0,0 +1,37 @@
;;;; conditions.lisp -- MCIAS error condition hierarchy
(in-package #:mcias-client)
(define-condition mcias-error (error)
((status :initarg :status :reader mcias-error-status
:documentation "HTTP status code (integer).")
(message :initarg :message :reader mcias-error-message
:documentation "Server error message string."))
(:report (lambda (c s)
(format s "MCIAS error ~A: ~A"
(mcias-error-status c)
(mcias-error-message c))))
(:documentation "Base condition for all MCIAS API errors."))
(define-condition mcias-auth-error (mcias-error) ()
(:documentation "401 Unauthorized -- token missing, invalid, or expired."))
(define-condition mcias-forbidden-error (mcias-error) ()
(:documentation "403 Forbidden -- insufficient role."))
(define-condition mcias-not-found-error (mcias-error) ()
(:documentation "404 Not Found -- resource does not exist."))
(define-condition mcias-input-error (mcias-error) ()
(:documentation "400 Bad Request -- malformed request."))
(define-condition mcias-conflict-error (mcias-error) ()
(:documentation "409 Conflict -- e.g. duplicate username."))
(define-condition mcias-server-error (mcias-error) ()
(:documentation "5xx -- unexpected server error."))
(defun signal-mcias-error (status message)
"Signal the appropriate MCIAS condition for STATUS (integer) and MESSAGE (string)."
(case status
(401 (error 'mcias-auth-error :status status :message message))
(403 (error 'mcias-forbidden-error :status status :message message))
(404 (error 'mcias-not-found-error :status status :message message))
(400 (error 'mcias-input-error :status status :message message))
(409 (error 'mcias-conflict-error :status status :message message))
(t (error 'mcias-server-error :status status :message message))))