feat(brands): self-serve activate-brand endpoint | LLMO-5605#2706
Conversation
POST /v2/orgs/:spaceCatId/brands/:brandId/activate — explicit, customer-triggered "make this brand live": resolve the brand's onboarded primary site, set it active with baseSiteId in one write, optionally generate AI prompts (a direct prompt_generation_base_url job, source 'brand-activation' — never Brandalf, never the llmo-customer-analysis cascade), and ensure the recurring brand-presence schedule (prompts-gated, via the shared createBrandPresenceSchedule helper). - Auth: membership + PAID entitlement (no admin requirement, mirrors Piece 1). - Idempotent: in-flight listJobs check before prompt-gen; schedule POST tolerates 409. - Returns 200 quickly (prompts generate async). 400/403/404/409 per the contract. - OpenAPI path + schemas; 24 unit tests; bumps spacecat-shared-drs-client to 1.13.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Mysticat review failed: Claude CLI crashed (exit 1): stderr= stdout={"type":"result","subtype":"error_max_budget_usd","duration_ms":5,"duration_api_ms":1694089,"is_error":true,"num_turns":1,"stop_reason":null,"session_id":"6122fae3-68a9-48bb-90ae-a503adffa0c8","total_cost_usd":12.047866350000003,"usage":{"input_tokens":0,"cache_creation_input_tokens":0,"cache_read_i |
|
Mysticat review failed: Claude CLI crashed (exit 1): stderr= stdout={"type":"result","subtype":"error_max_budget_usd","duration_ms":3,"duration_api_ms":1799198,"is_error":true,"num_turns":1,"stop_reason":null,"session_id":"637454f6-7c99-43b2-8e09-a0d743e3fdce","total_cost_usd":11.398289300000004,"usage":{"input_tokens":0,"cache_creation_input_tokens":0,"cache_read_i |
…ry test | LLMO-5605 CI build (full suite) caught two route-registry assertions the new endpoint must satisfy: - required-capabilities.js: map POST /v2/orgs/:spaceCatId/brands/:brandId/activate to organization:write (same as the sibling brand-write routes) so it isn't flagged as an unclassified route. - test/routes/index.test.js: add the route to the expected static/dynamic route set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
This PR will trigger a minor release when merged. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…on) | LLMO-5605 Resolve conflicts from main's brand work (status-transition endpoint, demotion guards, primary-URL changes, and @ts-check enabled on brands.js): - brands.js: keep BOTH activateBrandForOrg (this PR) and transitionBrandStatusForOrg (main); reconcile imports (BrandGovernanceClient, metrics-emf). - Move the PAID-entitlement check to src/support/llmo-paid-gate.js so the now @ts-check'd brands.js need not import Entitlement (absent from the package .d.ts). - activate: pass audience explicitly, use createResponse for the object body, and truthy spreads — to satisfy tsc --checkJs. - OpenAPI: keep both /activate and /status path blocks; regenerate docs/index.html. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1. Abstract
Adds
POST /v2/orgs/{spaceCatId}/brands/{brandId}/activate— the explicit, customer-triggered "make this brand live" action: resolves the onboarded primary site, sets the brand active withbaseSiteIdin the same write, optionally generates AI prompts, and ensures the recurring brand-presence schedule. LLMO-5605, Piece 2.2. Reasoning
Piece 2 of self-serve domain onboarding (LLMO-3749 / LLMO-5605). After Piece 1 stands up the site entity, the customer needs an explicit action to activate a (e.g. pending) brand whose primary URL is already onboarded — optionally kicking off AI-prompt generation and the recurring brand-presence schedule.
3. High-level overview of the changes
New endpoint
POST /v2/orgs/{spaceCatId}/brands/{brandId}/activate. For an existing (e.g. pending) brand whose primary URL is already onboarded:Site→ set the brandactivewithbaseSiteIdin the same write (400if no onboarded site;409onbrands_base_site_unique).generatePrompts: submit a directprompt_generation_base_urlDRS job withsource: 'brand-activation'(never Brandalf, never thellmo-customer-analysiscascade). Idempotent via an in-flightlistJobscheck.createBrandPresenceSchedulehelper (POST + tolerate 409).{ brandId, status:'active', baseSiteId, promptGenerationJobId?, scheduleId? }); prompts generate async.hasAccess) + explicit PAID entitlement (TierClient), mirroring Piece 1 — no admin requirement (decision: don't 403 paying non-admin members).api.yaml/brands-v2-api.yaml/schemas.yaml) + generated docs updated; bumps@adobe/spacecat-shared-drs-client→ 1.13.0.4. Required information
5. Affected / used mysticat-workspace projects
listJobs(dedup) +createBrandPresenceSchedulefrom 1.13.0 — feat(drs-client): add listJobs + createBrandPresenceSchedule | LLMO-5605 spacecat-shared#1721.(site, provider_id, source)dedup needs thesourcefilter from https://github.com/adobe-rnd/llmo-data-retrieval-service/pull/2370 deployed.createBrandPresenceSchedulehelper with thellmo-customer-analysiscascade (fix(llmo-customer-analysis): create brand-presence schedule via shared drs-client helper | LLMO-5605 spacecat-audit-worker#2737); both must route through the same method to preserve DRS provider-set dedup.7. Test plan
(a) Local — unit coverage for: same-write activate (
200);generatePrompts: true→ one prompt job + one schedule;false/ no-prompts → neither; double-click idempotency; the400/403/404/409paths; and DRS-not-configured. Integration tests (test/it/) for the endpoint are still outstanding — CLAUDE.md requires IT for new endpoints; this is a pre-merge item.(b) Per-env (dev / stage): activate a pending brand whose primary URL is onboarded → confirm
status:'active'withbaseSiteIdset in one write; withgeneratePrompts:trueconfirm a singlesource:'brand-activation'prompt job and one brand-presence schedule (re-activate is idempotent —listJobsdedup + schedule 409 tolerated); withgeneratePrompts:falseconfirm neither is created; confirm a non-PAID or non-member caller is rejected.8. Deployment & merge order
Final consumer in the LLMO-5605 Piece 2 chain.
sourcefilter — depends-on (deploy): deploy to DRS before this endpoint's dedup discriminates by source — https://github.com/adobe-rnd/llmo-data-retrieval-service/pull/2370.brands.js/routes/index.jsoverlap the open in-repo/status(feat(brands): add explicit brand status-transition endpoint (LLMO-5587) #2621) and demotion-guard (feat(brands): guard against active->pending demotion in updateBrand/upsertBrand (LLMO-5587) #2637) work; land after those merge to avoid conflicts. Remaining pre-merge item: the integration tests above.Ordered sequence: drs-client 1.13.0 (done) + DRS source filter deployed → land after the in-repo
/status+ demotion-guard PRs → merge this endpoint; the audit-worker migration lands in parallel.🤖 Generated with Claude Code