Skip to content

Publish a Python client + types for the User Manager API (transport parity with the TS client) #1807

Description

@rainer-friederich

Summary

Provide a published, CI-verified Python client + types for the User Manager API in @adobe/spacecat-shared-user-manager-client, at transport parity with the TS client. This is the user-manager twin of #1805 (Project Engine): today the package generates Pydantic models only, they are not published (so nothing can depend on them), there is no Python transport client, and the generated Python is not drift-checked in CI. The concrete consumer needs both this and 1805 — see below.

What exists today (v1.4.0)

packages/spacecat-shared-user-manager-client:

  • Source spec spec/usermanager_swagger.yamlswagger2openapibuild/openapi3.json → overlay → generate:ts (src/generated/types.ts) + generate:pydantic (python/serenity_user_manager/, Pydantic v2 models).
  • The TS client (src/client.js + src/internal.js) exports only createSerenityUserManagerApiClient — a thin openapi-fetch wrapper that owns transport (Bearer auth authMiddleware, base-URL resolveBaseUrl, and a method-aware retry: isIdempotent/isRetryableStatus/parseRetryAfterMs, 20s clamp).

The gap (identical to 1805)

  1. No Python transport clientdatamodel-codegen emits models, not a client. No Python equivalent of src/client.js (auth, retry, base-URL).
  2. Not consumablepackage.json files: ['src'] excludes python/; no PyPI/git-branch distribution.
  3. No CI drift-checkgenerate:pydantic is manual, so the committed Python surface can silently lag the spec.

Consumer: the migration CLI needs BOTH clients

mysticat-data-service scripts/serenity_migration/semrush_write.py defines a single SemrushWriteClient that spans both Semrush APIs — the Project Engine (/enterprise/projects/api, tracked in 1805) and the User Manager (/enterprise/users/api). On the user-manager surface it hand-rolls: create_child_workspace (POST /v2/workspaces/{parent}/child), workspace_status + wait_workspace_created (readiness polling over the lifecycle states), get_ai_resources + transfer_resources, delete_workspace, rename_workspace, and family listing. Its retry (_RETRY_STATUSES = {429, 502, 503, 504}) is not method-aware — the same double-write-on-POST hazard called out in 1805, which this client's TS transport already avoids.

Because the hand-rolled client is one class over both APIs, retiring it requires the Python client from 1805 and this one. Filing them separately keeps each package's work self-contained; they should land together to fully unblock the consumer.

Scope / tasks

  • Add a thin Python transport client (over httpx) consuming the generated Pydantic models, replicating the TS client's behaviour: fixed /enterprise/users/api prefix + origin-only base-URL normalisation; Authorization: Bearer from a value-or-callable token source resolved once per request; method-aware retry (429 all methods; 5xx/network only for idempotent methods) with Retry-After, equal-jitter backoff, 20s clamp, safe body replay.
  • Choose and implement distribution (PyPI, or a types-py-style orphan git branch, or git+https subdirectory) and stop excluding python/ from what ships.
  • Wire generate:pydantic (and the client) into CI as a drift-check, mirroring the TS surface.
  • Minimal transport tests (auth, method-aware retry incl. Retry-After, base-URL) and, ideally, an e2e smoke against the existing user-manager mock (mock/).
  • Acceptance: mysticat-data-service SemrushWriteClient's /enterprise/users/api half is re-backed onto the shared Python client (the Project Engine half via 1805).

Explicitly out of scope

  • A "real domain client" elevation is NOT proposed here (unlike the Project Engine's Make the Project Engine TS client a real domain client (own the dimension-root model, stop triplicating it) #1806). That issue is justified by the dimension-root tag domain model being triplicated across api-service, elmo, and the migration CLI. The User Manager surface is workspace lifecycle + resource-transfer CRUD, whose only real domain logic (workspace-readiness state machine, resource-transfer shape) lives in the provisioning/migration path rather than being spread across multiple consumers — so a thin typed client is appropriate and there is little to consolidate. If a check later shows that readiness/transfer logic is in fact duplicated across consumers, a separate, smaller "own the workspace-readiness state machine" issue can be opened then.

Pointers

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions