You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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)
No Python transport client — datamodel-codegen emits models, not a client. No Python equivalent of src/client.js (auth, retry, base-URL).
Not consumable — package.jsonfiles: ['src'] excludes python/; no PyPI/git-branch distribution.
No CI drift-check — generate:pydantic is manual, so the committed Python surface can silently lag the spec.
Consumer: the migration CLI needs BOTH clients
mysticat-data-servicescripts/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-serviceSemrushWriteClient'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.
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:spec/usermanager_swagger.yaml→swagger2openapi→build/openapi3.json→ overlay →generate:ts(src/generated/types.ts) +generate:pydantic(python/serenity_user_manager/, Pydantic v2 models).src/client.js+src/internal.js) exports onlycreateSerenityUserManagerApiClient— a thinopenapi-fetchwrapper that owns transport (Bearer authauthMiddleware, base-URLresolveBaseUrl, and a method-aware retry:isIdempotent/isRetryableStatus/parseRetryAfterMs, 20s clamp).The gap (identical to 1805)
datamodel-codegenemits models, not a client. No Python equivalent ofsrc/client.js(auth, retry, base-URL).package.jsonfiles: ['src']excludespython/; no PyPI/git-branch distribution.generate:pydanticis manual, so the committed Python surface can silently lag the spec.Consumer: the migration CLI needs BOTH clients
mysticat-data-servicescripts/serenity_migration/semrush_write.pydefines a singleSemrushWriteClientthat 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, andfamilylisting. 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
httpx) consuming the generated Pydantic models, replicating the TS client's behaviour: fixed/enterprise/users/apiprefix + origin-only base-URL normalisation;Authorization: Bearerfrom a value-or-callable token source resolved once per request; method-aware retry (429 all methods; 5xx/network only for idempotent methods) withRetry-After, equal-jitter backoff, 20s clamp, safe body replay.types-py-style orphan git branch, orgit+httpssubdirectory) and stop excludingpython/from what ships.generate:pydantic(and the client) into CI as a drift-check, mirroring the TS surface.Retry-After, base-URL) and, ideally, an e2e smoke against the existing user-manager mock (mock/).mysticat-data-serviceSemrushWriteClient's/enterprise/users/apihalf is re-backed onto the shared Python client (the Project Engine half via 1805).Explicitly out of scope
Pointers
packages/spacecat-shared-user-manager-client(@adobe/spacecat-shared-user-manager-client, v1.4.0); specspec/usermanager_swagger.yaml.src/client.js,src/internal.js(already method-aware —isIdempotent/isRetryableStatus/parseRetryAfterMs).mysticat-data-servicescripts/serenity_migration/semrush_write.py(SemrushWriteClient,/enterprise/users/apimethods).