feat: Serenity Data Insights topics + prompts endpoints | LLMO-6418#2885
Conversation
…points (LLMO-6418) Add two Serenity/Semrush brand-presence endpoints backing the Data Insights table: - GET .../serenity/brand-presence/topics — per-topic aggregates + each topic's embedded prompts - GET .../serenity/brand-presence/topics/:topicId/prompts — per-prompt drill-down (paged) Both are built from the single PROMPTS_BY_TOPIC element (78864493) — grouped by topic (server-side aggregation) / scoped via CBF_topic — rather than the wiki's 3-element fan-out (141adc88 currently 500s, 0564b061 lacks position; the live MFE also uses only 78864493). Adds unit tests for both definitions plus OpenAPI + contract-test coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eHandlers expectation The route-segregation test asserts the exact dynamic-route set; register the two new brand-presence routes so it matches getRouteHandlers output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
This PR will trigger a minor release when merged. |
…points (LLMO-6418) - Sort the topic drill-down (getTopicPrompts) by volume desc so it matches the order of the same topic's prompts embedded in getTopics (was raw element order). - Drop the c8-ignore on topic-prompts.js and topics-insights.js — both are fully unit-tested (100% stmts/branches/funcs/lines); remove a dead promptCount ternary and add a non-string/whitespace topic test to keep branch coverage at 100%. - Skip blank/whitespace-only topics in aggregation (consistent with the drill-down controller rejecting an empty topicId). - Fix stale "topics.js" doc references (the code lives in topics-insights.js). - Document that per-topic promptCount reflects the returned population and can only lag the drill-down's totalCount if the workspace exceeds the element's row cap. Waived (consistent with sibling serenity element endpoints, noted in PR): controller methods remain c8-ignored (POC glue); /topics intentionally embeds prompts for the eager table; visibility 0 is a real value included in the mean while position/sentiment sentinels are excluded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Hey @calvarezg,
Verdict: Approve - clean, well-structured addition of two new read endpoints that follow all established patterns.
Complexity: HIGH - large diff (1300+ lines across 16 files); API surface + FACS classification touched.
Changes: Adds two Serenity brand-presence Data Insights endpoints (per-topic table and per-prompt drill-down) backed by the PROMPTS_BY_TOPIC element, with full OpenAPI specs, FACS/capability wiring, and definition-layer unit tests (16 files).
Note: CI integration test (it-postgres) is still pending - verify it passes before merge.
Non-blocking (4): minor issues and suggestions
- nit: The region-resolution block in
listTopicsandlistTopicPromptsis identical (lines 646-661 and 729-744 of the controller diff) - consider extracting into a shared helper to reduce duplication as more brand-presence endpoints land. -src/controllers/elements.js - suggestion:
decodeURIComponentinlistTopicPrompts(line 705) is defensive against double-encoding butenrichPathInfoalready decodes. If it ever encounters a topic name containing a literal%2F(URL-encoded slash), the extra decode will corrupt it. Consider removing and trusting the middleware. -src/controllers/elements.js - nit: The
promptsarray is embedded in every topic row in thelistTopicsresponse, which means the full per-prompt dataset is returned twice when a client fetches topics then drills down. For large workspaces this could be substantial payload. The OpenAPI spec already marks it optional in spirit (not inrequired), so a future?embed=promptsquery param could let the UI opt in. -docs/openapi/schemas.yaml - nit:
Number(row?.volume) || 0intransformTopicPromptsResponse(line 967) coercesvolume: 0correctly (to 0), but would also coercevolume: NaNto 0 silently. The upstream element contract makes this unlikely but the siblingtoNumberOrNullfunction handles theNaNcase explicitly - consider consistency. -src/support/elements/definitions/topic-prompts.js
Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 8m 42s | Cost: $2.67 | Commit: 4b00d8ca5675d425b927bc4389b0f34f69a75794
If this code review was useful, please react with 👍. Otherwise, react with 👎.
…romtps # Conflicts: # docs/index.html
# [1.680.0](v1.679.1...v1.680.0) (2026-07-22) ### Bug Fixes * **llmo:** 404 brand-claims when the S3 object is missing ([#2887](#2887)) ([9004c4b](9004c4b)) ### Features * bind Semrush markets to SpaceCat Site entities (LLMO-6405) ([#2888](#2888)) ([f4f891d](f4f891d)) * Serenity Data Insights topics + prompts endpoints | LLMO-6418 ([#2885](#2885)) ([720e9f0](720e9f0))
|
🎉 This PR is included in version 1.680.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
Adds two Serenity (Semrush-backed) brand-presence endpoints for the Data Insights table: a per-topic table and a per-prompt drill-down. Backend only — the SR UI wiring (
project-elmo-ui) follows in a separate PR.Both endpoints are built from the single
PROMPTS_BY_TOPICelement (78864493) rather than the wiki's 3-element per-topic fan-out:141adc88(citations) currently returns 500,0564b061lacks apositionfield, and the live Brand Presence MFE also builds its view from78864493alone (verified via live probe + network capture, 2026-07-21).Endpoints
GET /v2/orgs/:spaceCatId/brands/:brandId/serenity/brand-presence/topics— per-topic aggregates (promptCount, brand mentions/citations, avg visibility/position/sentiment) with each topic's prompts embedded, for the eager Data Insights table. Brand sub-workspace scoped; optional region/date; returns the full topic list.GET /v2/orgs/:spaceCatId/brands/:brandId/serenity/brand-presence/topics/:topicId/prompts— per-prompt drill-down, scoped viaCBF_topic(the topic NAME — Semrush topics have no UUID), with server-side pagination.Notes / gaps (flagged, not silently guessed)
sourceCount/ share-of-voice aren't available from78864493(dedicated elements unusable) — left unset (optional in the UI contract).position: -1(unranked) andsentiment: nullare not averaged; a topic with no ranked/sentiment-bearing prompt yieldsnull.c8 ignoreper the sibling convention; unit tests are still included.Test plan
topic-prompts+topics-insightsdefinition unit tests (28 cases): payload shape,CBF_topicscoping, sentinel handling, aggregation, volume-sorted embedding.serenity-api.test.js): both new operations validate against their response schemas (24 passing).npm run docs:lint+docs:buildclean; route/capability suites pass (3189).API spec checklist
api.yaml,serenity-api.yaml,schemas.yaml); representative request/response shapes are exercised by the contract-test fixtures.Related Issues
https://jira.corp.adobe.com/browse/LLMO-6418