Skip to content

feat(user-manager-client): vendor Semrush User Manager spec + generated types (foundation slice)#1680

Merged
byteclimber merged 15 commits into
mainfrom
feat/user-manager-client
Jun 23, 2026
Merged

feat(user-manager-client): vendor Semrush User Manager spec + generated types (foundation slice)#1680
byteclimber merged 15 commits into
mainfrom
feat/user-manager-client

Conversation

@byteclimber

Copy link
Copy Markdown
Contributor

What

Adds @adobe/spacecat-shared-user-manager-client — the foundation slice for typed integration with Semrush's User Manager API (/enterprise/users/api). Mirrors the Project Engine foundation (LLMO-5461), applied to the larger User Manager API (LLMO-5558).

A private, types-only package (identical shape to spacecat-shared-project-engine-client):

  • Vendored specspec/usermanager_swagger.yaml (Swagger 2.0, 234 paths / 284 operations, 187 Auth-Data-Jwt user-facing routes + admin API-Key routes).
  • Generation pipeline behind npm run generate: swagger2openapi --patchopenapi-typescript (src/generated/types.ts, all ops) + datamodel-codegen (python/serenity_user_manager/, Pydantic v2).
  • Foundation test asserting the vendored spec contract (Swagger 2.0, basePath, Auth-Data-Jwt) and key generated paths.

What's deferred (follow-up PRs, per the Project Engine sequencing)

  • The thin UserManagerApiClient (openapi-fetch over the generated paths) with dual auth — caller's raw IMS token as Authorization: Bearer for user-facing routes, API-Key header for admin routes (and Auth-Data-Jwt narrowed out of the public surface).
  • The Counterfact stateful mock store.
  • Adoption in spacecat-api-service (swap the hand-rolled serenity/rest-transport.js USERS_API_PREFIX calls).

Notes

  • Base host is shared with Project Engine (SEMRUSH_PROJECTS_BASE_URL); only the basePath differs.
  • Generated artifacts are committed in their own commit and marked linguist-generated (.gitattributes), so they collapse in review.
  • datamodel-code-generator is a Python tool (not an npm dep): pip install datamodel-code-generator.

Validation

  • npm run generatetypes.ts (25.5k lines) + Pydantic package.
  • npm test → 7 passing (foundation).
  • npm run lint → clean.

🤖 Generated with Claude Code

byteclimber and others added 3 commits June 15, 2026 19:24
…ion pipeline

Foundation slice for @adobe/spacecat-shared-user-manager-client, mirroring the
Project Engine foundation (LLMO-5461) applied to the larger User Manager API
(LLMO-5558). Private, types-only package: vendors the Swagger 2.0 spec
(/enterprise/users/api, 234 paths / 284 operations, 187 Auth-Data-Jwt routes)
and wires the swagger2openapi --patch -> openapi-typescript / datamodel-codegen
pipeline behind `npm run generate`. The openapi-fetch client wrapper with dual
auth (IMS bearer + API-Key) and the counterfact mock store land in follow-up PRs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generated by `npm run generate` from the vendored spec. Committed in its own
commit and marked linguist-generated (see .gitattributes) so it collapses in
review and is excluded from diff counts.

- src/generated/types.ts: openapi-typescript 7.13.0 paths/components (all ops)
- python/serenity_user_manager/: datamodel-codegen pydantic_v2 package

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds @adobe/spacecat-shared-user-manager-client and its generation-pipeline
devDeps (openapi-typescript, swagger2openapi, counterfact) to the root lockfile
so `npm ci` stays in sync in CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

This PR will trigger a minor release when merged.

@byteclimber byteclimber requested a review from MysticatBot June 15, 2026 17:37

@MysticatBot MysticatBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @byteclimber,

Verdict: Request changes - one blocking finding on test assertion quality; the rest is clean.
Changes: Vendors the Semrush User Manager Swagger 2.0 spec and wires a generation pipeline producing TypeScript and Pydantic types as a new private foundation-slice package (21 files).

Must fix before merge

  1. [Important] Generated-types test assertions too generic to catch spec drift - test/foundation.test.js:69-77 (details inline)
Non-blocking (3): minor issues and suggestions
  • suggestion: Use a different port for the mock script to avoid collision with project-engine-client (both use 4010) - package.json:21
  • nit: publishConfig.access: "public" is a no-op while private: true is set; remove or document the intent - package.json:38
  • suggestion: Add spec/ to .npmignore as defense-in-depth against accidental spec publication if the package ever goes public - .npmignore

Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 1m 31s | Cost: $4.98 | Commit: 8733f50d3f3f8a678f39391b3b018194b78b017b
If this code review was useful, please react with 👍. Otherwise, react with 👎.

@MysticatBot MysticatBot added the ai-reviewed Reviewed by AI label Jun 15, 2026
…ests

Addresses mysticat-bot review: `/child` and `/status` were too generic
and would pass against any large OpenAPI types file. Replace with the
full, unambiguous path templates that only exist in this spec.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@byteclimber byteclimber requested a review from MysticatBot June 15, 2026 17:58

@MysticatBot MysticatBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @byteclimber,

Verdict: Approve - prior blocking finding addressed, clean foundation slice.
Changes: Vendors the Semrush User Manager Swagger 2.0 spec and wires a generation pipeline producing TypeScript and Pydantic types as a new private foundation-slice package (21 files).

Non-blocking (1): minor issues and suggestions
  • nit: Root CLAUDE.md states "containing 23 packages" which becomes 24 after this PR merges - update the count or remove the hard number - CLAUDE.md

Previously flagged, now resolved

  • Generated-types test assertions now use spec-specific path fragments (/v2/workspaces/{id}/child, /v1/workspaces/{id}/status) that discriminate against wrong-spec wiring.

Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 0m 33s | Cost: $3.53 | Commit: 1f346914ee06607a5294a27d4ba102d8c6363a76
If this code review was useful, please react with 👍. Otherwise, react with 👎.

Auth-Data-Jwt appears on ~187 operations in the vendored spec but is a
vendor spec artifact — the live Adobe gateway authenticates on
Authorization: Bearer only (same finding as rainer-friederich CR2 on
Project Engine PR #1661, 2026-06-15). Sending Auth-Data-Jwt returns 401.

Update README and test label to make clear this is a spec artifact, not
the live auth contract. A generation-time overlay to strip it from
generated types follows in a later PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@byteclimber byteclimber requested a review from MysticatBot June 16, 2026 07:25

@MysticatBot MysticatBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @byteclimber,

Verdict: Approve - clean documentation fix, no blocking concerns.
Changes: Clarifies that Auth-Data-Jwt is a vendor spec artifact (not sent to the live gateway) in README and test descriptions (21 files).

Non-blocking (2): minor issues and suggestions
  • nit: Blockquote break in the "Spec auth correction" callout - the sentence starting "Refresh is manual..." falls outside the > prefix, making it render as body text rather than part of the callout - packages/spacecat-shared-user-manager-client/README.md:38
  • nit: Root CLAUDE.md states "containing 23 packages" which becomes 24 after this PR merges - update the count or remove the hard number - CLAUDE.md

Previously flagged, now resolved

  • Generated-types test assertions now use spec-specific path fragments (/v2/workspaces/{id}/child, /v1/workspaces/{id}/status) that discriminate against wrong-spec wiring.

Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 0m 46s | Cost: $2.41 | Commit: ce524e41219635cfe984b78f820c821f31017d56
If this code review was useful, please react with 👍. Otherwise, react with 👎.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
rainer-friederich and others added 6 commits June 22, 2026 23:03
Mirror the project-engine-client overlay machinery so the vendored Semrush
User Manager swagger can be corrected at generation time without ever editing
the pristine vendored file (kept faithful for re-vendoring).

scripts/apply-overlay.mjs is a small generic OpenAPI-Overlay applier (update /
remove with a tiny JSONPath dialect) that reads spec/overlays/corrections.yaml
and rewrites the swagger2openapi output build/openapi3.json in place. The YAML
is the single source of truth for the corrections; the script just executes it.
Wired into `npm run generate` as the spec:overlay step (js-yaml devDep added).

Two corrections, both verified against the live API and its api-service
transport (src/support/serenity/rest-transport.js):

- CR1: the spec models Auth-Data-Jwt as a required header on ~187 operations,
  but the live API authenticates on Authorization: Bearer <IMS> only (Semrush
  accepts the IMS bearer directly). Remove Auth-Data-Jwt from every operation
  and add an imsBearer security scheme + global security requirement.
- CR2: GET /v1/workspaces/{id}/status is typed as an array of
  WorkspaceCheckResponse, but the live API returns a single object
  ({ status: "not ready" | "created" | "error" }) — the transport reads it as
  status.status === 'created'. Drop the array wrapper.

Regenerated src/generated/types.ts (Auth-Data-Jwt header gone from all ops,
status 200 now a single object; path/operation/schema counts unchanged). The
Pydantic models are unaffected (the overlay touches auth headers and a response
envelope, neither of which is a component schema). Added overlay-guard tests
pinning CR1 and CR2 against the generated surface; reworded the vendored-spec
Auth-Data-Jwt test to drop the inaccurate "Adobe gateway" framing.

LLMO-5461

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the js-yaml entry to package-lock.json to match the devDependency added
in the previous commit. Fixes the CI "Verify package-lock.json is in sync" gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
README was written for a pre-overlay foundation slice and described the
correction overlay as a follow-up; it also carried the inaccurate "Adobe
gateway" framing and an unverified admin/API-Key auth split. Rewrite it to
match what this PR actually ships:

- the overlay is present, not a follow-up; added a "Spec corrections" section
  documenting CR1 (strip Auth-Data-Jwt, add imsBearer — Semrush accepts the IMS
  bearer directly) and CR2 (GET /v1/workspaces/{id}/status returns a single
  object, not an array);
- dropped the "Adobe gateway"/token-exchange fiction and the unverified
  API-Key admin-route claim (no gateway exists; the live API takes Bearer IMS);
- wired the overlay step into the pipeline diagram + commands table; fixed a
  run-on sentence in the spec-source note; the client wrapper + stateful mock
  remain the genuine follow-ups.

Also sync the overlay applier with the project-engine-client copy (LLMO-5461),
which was refactored to be unit-testable: scripts/apply-overlay.mjs now exports
its pure functions and runs IO only via a guarded main(), and a 0-match remove
warns (stale-correction signal) while a 0-match update still throws. Ported the
matching test/overlay.test.js (added sinon devDep). The two copies now differ
only by their header comment; a shared extraction is a tracked follow-up.

Suite: 24 passing, lint clean, package-lock in sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- README: drop the "this PR vendors…" framing — a README is durable package
  docs, not a PR description, and that line reads wrong the moment it merges.
  Replaced with a roadmap note ("Not here yet: …").
- Sync the overlay applier with the project-engine-client copy (LLMO-5461),
  which was refactored again in review: applyOverlay() now returns a structured
  result per action ({ target, remove, hits, staleRemove }) and prints nothing —
  the CLI main() does all user-facing logging. The two copies again differ only
  by their header comment.
- overlay.test.js asserts the structured result (no console stubbing), so it no
  longer needs sinon — dropped the now-unused sinon devDep.

Suite: 24 passing, lint clean, package-lock in sync; regenerated types.ts is
byte-identical (the refactor is output-equivalent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@rainer-friederich rainer-friederich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@rainer-friederich

Copy link
Copy Markdown
Contributor

@byteclimber — summary of what changed on this branch after your last commit (061b0cab, the main-merge on top of the foundation slice), so the diff isn't a surprise.

The foundation slice (vendored spec + generated types + foundation test) is yours. Everything after 061b0cab adds the generation-time spec-correction overlay and brings the package to a mergeable state. It mirrors the Project Engine overlay (same machinery, applied to this API):

#1661

1. Spec-correction overlay + corrections (48c09887). A small OpenAPI Overlay (spec/overlays/corrections.yaml), applied to the converted build/openapi3.json at generation time by scripts/apply-overlay.mjs; the vendored spec/usermanager_swagger.yaml is never touched. Wired into npm run generate as the spec:overlay step (added the js-yaml devDep). Two corrections, both verified against the live API and the deployed api-service transport (rest-transport.js):

  • CR1 — auth: the spec models Auth-Data-Jwt as a required header on ~187 operations, but the live API authenticates on Authorization: Bearer <IMS> only — Semrush accepts the IMS bearer directly. The overlay removes Auth-Data-Jwt from every operation and adds an imsBearer security scheme.
  • CR2 — status shape: GET /v1/workspaces/{id}/status is typed as an array of WorkspaceCheckResponse, but the live API returns a single object ({ status: "not ready" | "created" | "error" }) — the transport reads status.status === 'created'. The overlay drops the array wrapper.

Regenerated src/generated/types.ts (Auth-Data-Jwt gone from all ops, status 200 now a single object; path/operation/schema counts unchanged). Pydantic models unaffected. Added overlay-guard tests pinning CR1 + CR2 against the generated surface.

2. README to mergeable + durable (b4e4848d, dbd38ef6). The README described the overlay as a follow-up and carried an inaccurate "Adobe gateway" / token-exchange framing plus an unverified admin/API-Key auth split. Rewrote it: documented the overlay as present (new "Spec corrections" section + pipeline/commands table), removed the gateway fiction (there is no gateway — Semrush takes the IMS bearer directly), and dropped the "this PR…" wording (a README is durable package docs, not a PR description).

3. Overlay applier kept in lockstep with the Project Engine copy. scripts/apply-overlay.mjs is byte-identical to the Project Engine one except its header comment; it picked up that PR's review-driven refactors (exported pure functions + guarded main(), then a structured applyOverlay return so the function prints nothing). Added test/overlay.test.js. A shared extraction (@adobe/spacecat-shared-openapi-overlay) is the tracked follow-up once one of the two lands.

@MysticatBot MysticatBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @byteclimber,

Verdict: Approve - clean foundation slice, prior blocking finding addressed, no new blocking concerns.
Changes: Vendors the Semrush User Manager Swagger 2.0 spec, adds a spec-correction overlay (CR1: auth scheme, CR2: response shape), wires a generation pipeline producing TypeScript and Pydantic types, and tests both the overlay logic and the generated surface (25 files).

Non-blocking (4): minor issues and suggestions
  • suggestion: Guard deepMerge against prototype-pollution keys (__proto__, constructor) - low risk today since the overlay is hand-authored, but trivial hardening if the function is reused - scripts/apply-overlay.mjs:121
  • suggestion: Add a preflight check to the generate script (or split into generate:ts-only) so devs who lack datamodel-codegen on PATH get a clear error instead of a partial pipeline failure - package.json:22
  • nit: The CR2 overlay actions (remove then update) are order-dependent - a brief YAML comment noting "order matters" would prevent accidental reordering - spec/overlays/corrections.yaml:42
  • nit: Root CLAUDE.md states "containing 23 packages" which becomes 24 after this PR merges - update the count or remove the hard number - CLAUDE.md

Previously flagged, now resolved

  • Generated-types test assertions now use spec-specific path fragments (/v2/workspaces/{id}/child, /v1/workspaces/{id}/status) that discriminate against wrong-spec wiring.

Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 2m 53s | Cost: $4.67 | Commit: dbd38ef641c43e90f8171eb20a442f68c939750d
If this code review was useful, please react with 👍. Otherwise, react with 👎.

From MysticatBot's approving review:
- apply-overlay deepMerge now skips prototype-pollution keys (__proto__,
  constructor, prototype) — trivial hardening for the (hand-authored today,
  reused tomorrow) overlay applier; covered by a new test.
- generate:pydantic preflights for datamodel-codegen on PATH and fails with a
  clear "pip install datamodel-code-generator" message instead of a cryptic
  partial-pipeline 127.
- corrections.yaml CR2: documented that the remove-then-update pair is
  order-dependent (update deep-merges, so removing the array wrapper must run
  first) to prevent accidental reordering.
- root CLAUDE.md: dropped the brittle hard-coded package count ("23 packages")
  so it can't drift as packages are added.

24 passing + new guard test, lint clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rainer-friederich

Copy link
Copy Markdown
Contributor

Addressed the 4 non-blocking items from the approval (a3544518):

  • deepMerge prototype-pollution guardapply-overlay.mjs now skips __proto__/constructor/prototype keys (no legitimate OpenAPI node key is one of those). New test asserts Object.prototype stays clean. Carried into the Project Engine copy too, so the two appliers stay identical.
  • generate:pydantic preflight — checks datamodel-codegen is on PATH and fails with a clear pip install datamodel-code-generator message instead of a cryptic partial-pipeline exit 127.
  • CR2 order-dependence — added a YAML comment to corrections.yaml noting the remove-then-update pair must not be reordered (update deep-merges, so the array wrapper has to be removed first).
  • Brittle package count — dropped the hard-coded 23 packages from the root CLAUDE.md so it can't drift.

24 passing + the new guard test, lint clean. Re-requesting review.

The structure diagram still said "23 npm workspace packages" after the prose
count was removed; drop the number there too so neither can drift.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rainer-friederich rainer-friederich requested review from MysticatBot and removed request for MysticatBot June 22, 2026 22:14

@MysticatBot MysticatBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @byteclimber,

⚠ Degraded review - no spec document was found for this change (searched the PR links, the touched repos' docs, the architecture/guidelines docs, and linked Jira). This review covers code-level quality but could not validate the change against an agreed design, so confidence is reduced. Add a spec link (PR template section 4) and re-request review for a full-confidence pass.

Verdict: Approve - prior non-blocking suggestions all addressed cleanly, no new concerns.
Changes: Adds prototype-pollution guard to deepMerge, preflight check for datamodel-codegen, overlay ordering comment, and CLAUDE.md package-count fix (5 files).

Non-blocking (1): minor issues and suggestions
  • nit: CLAUDE.md Monorepo Structure tree still says "23 npm workspace packages" but should be 24 after this PR merges (the Architecture Overview paragraph was fixed, but the tree diagram was not) - CLAUDE.md

Previously flagged, now resolved

  • deepMerge prototype-pollution guard implemented with Set-based key blocklist and test exercising JSON.parse bypass of V8 proto optimization.
  • generate:pydantic script now fails fast with a clear error when datamodel-codegen is not on PATH.
  • CR2 overlay actions now carry an ordering comment explaining why remove must precede update.
  • Architecture Overview paragraph no longer hardcodes the package count.

Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 1m 22s | Cost: $2.99 | Commit: a35445188d1c569795683ece56051fe20c97c151
If this code review was useful, please react with 👍. Otherwise, react with 👎.

@MysticatBot MysticatBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @byteclimber,

⚠ Degraded review - no spec document was found for this change (searched the PR links, the touched repos' docs, the architecture/guidelines docs, and linked Jira). This review covers code-level quality but could not validate the change against an agreed design, so confidence is reduced. Add a spec link (PR template section 4) and re-request review for a full-confidence pass.

Verdict: Approve - prior nits cleanly addressed, no new concerns.
Changes: Addresses four non-blocking review suggestions (deepMerge prototype-pollution guard, pydantic preflight, overlay order comment, CLAUDE.md count fix) across 5 files.

Previously flagged, now resolved

  • deepMerge now skips prototype-pollution keys (__proto__, constructor, prototype) with a Set guard and accompanying test.
  • generate:pydantic preflights for datamodel-codegen on PATH with a clear install message on failure.
  • CR2 overlay actions documented as order-dependent (remove before update to avoid invalid mixed schema).
  • Root CLAUDE.md dropped the brittle hard-coded package count ("23 packages").

Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 7m 58s | Cost: $2.35 | Commit: 187e6f52699ce58fe874e73abfade00aeaefeed6
If this code review was useful, please react with 👍. Otherwise, react with 👎.

@byteclimber byteclimber merged commit e5344a7 into main Jun 23, 2026
6 checks passed
@byteclimber byteclimber deleted the feat/user-manager-client branch June 23, 2026 07:37
solaris007 pushed a commit that referenced this pull request Jun 23, 2026
## @adobe/spacecat-shared-user-manager-client-v1.0.0 (2026-06-23)

### Features

* **user-manager-client:** vendor Semrush User Manager spec + generated types (foundation slice) ([#1680](#1680)) ([e5344a7](e5344a7))
@solaris007

Copy link
Copy Markdown
Member

🎉 This PR is included in version @adobe/spacecat-shared-user-manager-client-v1.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants