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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions