Skip to content

SDK Python: client generato, autenticazione OAuth2, CLI e CI - #1

Merged
svdigital-development merged 3 commits into
mainfrom
claude/first-install
Sep 30, 2026
Merged

svdigital-development merged 3 commits into
mainfrom
claude/first-install

Conversation

@svdigital-development

Copy link
Copy Markdown
Member

Prima versione dell'SDK Python ufficiale delle API pubbliche Shellrent (shellrent-sdk su PyPI, package shellrent_sdk), con la stessa impostazione dell'SDK PHP: client generato dalla spec, a mano solo autenticazione, creazione del client, CLI, test, documentazione e CI.

Commit

  1. Spec – spec/openapi.yaml aggiornata alla versione 3.1 prodotta dall'applicazione.
  2. Codice generato – src/shellrent_sdk/client/ generato da bin/generate con openapi-python-client 0.29.1 e Ruff, entrambi con versione fissata ed eseguiti con uvx. Non va revisionato riga per riga: la CI lo rigenera e fallisce se differisce.
  3. Strato scritto a mano – da rivedere con attenzione:
    • shellrent_sdk.auth: ClientCredentials, TokenStore (MemoryTokenStore, FileTokenStore) e OAuth2Auth, un httpx.Auth che chiede il token dal flusso di autenticazione, lo riusa fino a 60 s prima della scadenza, ripete una volta dopo un 401 ed è thread-safe.
    • shellrent_sdk.http: RetryTransport, che ripete i 429 rispettando Retry-After (sync e async), e client_args().
    • create_client(), unwrap() e ApiException.
    • La CLI shellrent (shellrent api, shellrent token).
    • Test, README, CHANGELOG, CLAUDE.md, CI e rilascio.

Scelte da valutare

  • create_client() restituisce AuthenticatedClient e non Client: le funzioni delle operazioni protette lo richiedono nel tipo. Il token lo mette OAuth2Auth a ogni richiesta, non il campo token del client.
  • unwrap(op.sync_detailed(...)) restituisce il body tipizzato oppure solleva ApiException (status_code, error, headers, content), compreso il 429 che resta dopo i retry.
  • RetryTransport è un'unica classe sync e async, perché il client generato passa gli stessi httpx_args sia a httpx.Client sia a httpx.AsyncClient. Segue le variabili d'ambiente HTTP(S)_PROXY, ALL_PROXY, NO_PROXY, SSL_CERT_FILE e SSL_CERT_DIR come httpx. I proxy li legge con urllib.request, perché httpx non ha un'API pubblica per farlo.
  • FileTokenStore crea la cartella con permessi 0700 e i file con 0600. La CLI mette in cache il token su file per default; SHELLRENT_TOKEN_CACHE indica un'altra cartella oppure off.
  • auth, http e la CLI non importano il codice generato: li userà anche shellrent-internal-sdk, e test_layers.py lo verifica.

Verifica

  • Ruff, mypy strict e pytest (115 test) passano in locale su Python 3.11–3.14 e su 3.11 con le dipendenze minime (httpx 0.23.1, attrs 22.2.0).
  • I test su proxy e certificati usano un proxy e un server HTTPS locali. tests/fixtures/tls/server-key.pem è una chiave di prova, valida solo per 127.0.0.1/localhost; la chiave della CA non è stata conservata.
  • I test di integrazione non sono stati eseguiti. Partono solo con i secret SHELLRENT_CLIENT_ID e SHELLRENT_CLIENT_SECRET (più, se serve, la variabile SHELLRENT_API_URL); test_errors.py richiede lo scope purchases:read.

Prima del primo rilascio

  • Su PyPI: configurare il Trusted Publisher per shellrent-sdk (repository shellrent/shellrent-sdk-python, workflow release.yml, environment pypi).
  • Su GitHub: creare l'environment pypi con i revisori obbligatori e aggiungere i secret per i test di integrazione.
  • Il rilascio parte con un tag vX.Y.Z: release.yml builda, controlla che la versione coincida con il tag e pubblica dopo l'approvazione.

Aperto sulla spec

  • Va confermato che getHealth non sia soggetto a throttling: è l'unica operazione protetta senza 429 documentato.
  • Cosmetico: le risposte di payOrders e getHostingAntivirus sono schemi inline, quindi le classi si chiamano PayOrdersResponse200 e GetHostingAntivirusResponse200.

🤖 Generated with Claude Code

Tobia Zanarella and others added 3 commits September 30, 2026 22:31
Copied from the Shellrent application: nullable references as anyOf with
null, nullable data and meta in ApiError and in the inline envelopes,
required fields on the response schemas, 429 documented on the throttled
operations and on the token endpoint (OAuth2 format), OrderPayRequest and
SmsCreateRequest as plain objects, payOrders data as Payment or null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bin/generate runs the generator and Ruff, both pinned, through uvx, with
--fail-on-warning so that no operation is skipped silently, and replaces
src/shellrent_sdk/client entirely. The content type overrides return the
PDF, XML and p7m downloads as File with the raw bytes: without them the
PDFs are dropped and the XML is decoded as text.

The generated code is never edited by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- shellrent_sdk.auth: ClientCredentials, TokenStore with MemoryTokenStore
  and FileTokenStore (0700 directory, 0600 files, atomic writes), and
  OAuth2Auth, an httpx.Auth that requests the token from the auth flow,
  reuses it until 60 s before it expires, retries once after a 401 and is
  safe across threads and event loops. The secret is never cached.
- shellrent_sdk.http: RetryTransport (429 with Retry-After, sync and
  async, following the proxy and certificate environment like httpx) and
  client_args() for the httpx clients.
- create_client(), returning the generated AuthenticatedClient, and
  unwrap()/ApiException for the documented error responses.
- The shellrent command (shellrent api, shellrent token), modelled on
  gh api, with the token cached in a file.
- auth, http and the CLI do not depend on the generated code: they are
  used by shellrent-internal-sdk too.
- Packaging with hatchling and hatch-vcs, uv.lock, Ruff and strict mypy,
  pytest with httpx.MockTransport, CI on Python 3.11-3.14 plus the lowest
  dependencies and the regeneration check, release to PyPI with Trusted
  Publishing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@svdigital-development
svdigital-development merged commit 1dec8bd into main Sep 30, 2026
6 checks passed
@svdigital-development
svdigital-development deleted the claude/first-install branch September 30, 2026 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant