Skip to content

[CDX-709] Add Product Insights Agent tracking events - #176

Open
Mudaafi wants to merge 7 commits into
masterfrom
cdx-709-pia-tracking-events
Open

Mudaafi wants to merge 7 commits into
masterfrom
cdx-709-pia-tracking-events

Conversation

@Mudaafi

@Mudaafi Mudaafi commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

https://linear.app/constructor/issue/CDX-709/android-sdk-product-insights-agent-add-the-9-pia-tracking-events

Summary

Adds the 9 Product Insights Agent (PIA) tracking events, ported from the JS SDK. Each one takes a single request object with a builder, following the pattern from #173 (SearchSubmitTrackingData).

Method Endpoint
trackProductInsightsAgentFocus product_insights_agent_focus
trackProductInsightsAgentOutOfView product_insights_agent_out_of_view
trackProductInsightsAgentView product_insights_agent_view
trackProductInsightsAgentViews product_insights_agent_views
trackProductInsightsAgentQuestionClick product_insights_agent_question_click
trackProductInsightsAgentQuestionSubmit product_insights_agent_question_submit
trackProductInsightsAgentAnswerFeedback product_insights_agent_answer_feedback
trackProductInsightsAgentResultClick product_insights_agent_result_click
trackProductInsightsAgentAnswerView product_insights_agent_answer_view
ConstructorIo.trackProductInsightsAgentResultClick(
    ProductInsightsAgentResultClickTrackingData.build("rec-123", "Is it waterproof?", "pdp-456") { position = 0 }
)

How a request flows

flowchart TD
  A["App"] -->|"TrackingData.build { }"| B["ConstructorIo.trackProductInsightsAgentX(request)<br/><i>public, fire-and-forget</i>"]
  B --> C["...Internal(): Completable<br/>resolve section (default: defaultItemSection)<br/>merge analyticsTags with defaults<br/>build RequestBody: event fields + analytics_tags only"]
  C --> D["DataManager<br/><i>pass-through</i>"]
  D --> E["ConstructorApi (Retrofit)<br/>@POST · @Body JSON (Moshi) · section via @QueryMap"]
  E --> F["RequestInterceptor (OkHttp)<br/>runs on every SDK request"]
  F --> G["POST /v2/behavioral_action/product_insights_agent_x"]

  F -.- P["Adds query params:<br/>key · i · ui · s · us · ef-* · c · _dt<br/><br/>Redacts emails, phones and card numbers<br/>from behavioral paths and query"]

  classDef interceptor fill:#fff4d6,stroke:#c98a00,stroke-width:2px,color:#3d2a00
  classDef note fill:#fffbea,stroke:#c98a00,stroke-dasharray:4 3,color:#3d2a00
  class F interceptor
  class P note
Loading

The PIA code only builds the event fields. The client params (key, i, ui, s, us, ef-*, c, _dt) are added to the query of every SDK request by RequestInterceptor, which is where the spec expects them, so the request bodies don't repeat them.

Noteworthy Details

  • Focus and out of view share one request body, as do question click and question submit, since each pair sends the same fields.
  • items and followUpQuestions on answer view are capped at 100, matching the server limit.
  • viewTimespans take epoch milliseconds (System.currentTimeMillis()) and are sent as ISO-8601 UTC strings
  • Merge conflict expected with [CDX-475] Implement ASA tracking events #169
  • I considered doing Inherited Classes here, but because of the complexity of subclassing the builder (see: https://www.artima.com/weblogs/viewpost.jsp?thread=133275), I opted to go with simple interfaces instead to make objects reusable. Possible future refactor.

Tests

  • ConstructorIoProductInsightsAgentTrackingTest: 43 MockWebServer tests. Per event they check fields are present when set and absent when not.
  • Builder tests: one file per TrackingData class, plus a check that all 9 expose the shared fields through the interface.
  • ConstructorIoIntegrationTest: one live test per event against the test key. All 9 return 204.

The 3 ConstructorIoRecommendationsTest failures and the random SocketTimeoutExceptions in live *AgainstRealResponse tests happen on master too. They're unrelated to this change.

Port the 9 PIA tracking events from the JS SDK so native Android apps can
measure the Product Insights Agent. Each public method takes a single
TrackingData request object with a builder, following SearchSubmitTrackingData.

- section is sent as a query param on all 9 and defaults to defaultItemSection
- analyticsTags are merged with defaultAnalyticsTags
- items and followUpQuestions are capped at 100, the server limit
- view timespans take epoch ms and are sent as ISO-8601 UTC date-times
The endpoints validate bodies strictly and the public methods are
fire-and-forget, so a malformed body would fail as a silent 400.

- MockWebServer tests check field presence and omission per event, plus
  edge cases: position 0, empty answerText, the 100 caps, UTC timespans
- Builder tests cover each TrackingData class
- One live test per event confirms the server returns 204
Add a Product Insights Agent Events subsection to README section 12 with
a build { } example for each of the 9 events.
@Mudaafi
Mudaafi force-pushed the cdx-709-pia-tracking-events branch from d971b2f to 81d5146 Compare October 22, 2026 14:10
Address review feedback on the PIA tracking events.

- Drop c/i/s/key/ui/us/_dt/beacon/section from the request bodies.
  RequestInterceptor already adds the client params to the query, and
  section is sent as a query param, which is where the spec expects them.
- Add ProductInsightsAgentRequestBody holding item_id, item_name,
  variation_id, thread_id and analytics_tags for the 9 bodies.
- Add ProductInsightsAgentTrackingData with a self-typed Builder for the
  shared itemId/itemName/variationId/threadId/sectionName/analyticsTags,
  so chained setters keep the concrete builder type.
- Assert the client params on the query and their absence from the body.
The abstract base classes added a generic public builder and unchecked
casts while saving little, so go back to flat classes.

- Add a ProductInsightsAgentTrackingData interface for the fields every
  PIA event shares, implemented by the 9 TrackingData classes. Section and
  analytics tag resolution in ConstructorIo now goes through it.
- Make the request bodies flat data classes again, still without the
  client params that RequestInterceptor adds to the query.
- Share one body between focus and out_of_view, and one between
  question_click and question_submit, since each pair sends the same fields.
@Mudaafi
Mudaafi requested a review from pedro-lb October 26, 2026 14:10
@Mudaafi
Mudaafi marked this pull request as ready for review October 26, 2026 14:10
@Mudaafi
Mudaafi requested a review from a team as a code owner October 26, 2026 14:10
Copilot AI balanced review requested due to automatic review settings October 26, 2026 14:10
constructor-claude-bedrock[bot]

This comment was marked as outdated.

@Mudaafi
Mudaafi removed the request for review from pedro-lb October 26, 2026 14:12

Copilot AI 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.

🟡 Changes recommended

User question and answer text bypasses existing personal-information redaction when transmitted in JSON tracking bodies.

1 open issue
What changed in this PR

Adds nine Product Insights Agent tracking events to the Android SDK, following the single-request builder pattern introduced for search-submit tracking.

Changes:

  • Adds public tracking methods, request builders, JSON models, and API wiring.
  • Supports shared analytics tags, UTC view timestamps, and answer-view list limits.
  • Adds usage examples, builder tests, request-payload tests, and live integration tests.
File Description
README.md Documents all nine events with Kotlin examples.
library/​src/​test/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentViewTrackingDataTest.kt Tests view request construction.
library/​src/​test/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentViewsTrackingDataTest.kt Tests timespan request construction.
library/​src/​test/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentTrackingDataTest.kt Checks shared fields across all events.
library/​src/​test/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentResultClickTrackingDataTest.kt Tests result-click fields and zero position.
library/​src/​test/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentQuestionSubmitTrackingDataTest.kt Tests question-submit request construction.
library/​src/​test/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentQuestionClickTrackingDataTest.kt Tests question-click request construction.
library/​src/​test/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentOutOfViewTrackingDataTest.kt Tests out-of-view request construction.
library/​src/​test/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentFocusTrackingDataTest.kt Tests focus request construction.
library/​src/​test/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentAnswerViewTrackingDataTest.kt Tests answer-view fields and empty answers.
library/​src/​test/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentAnswerFeedbackTrackingDataTest.kt Tests feedback request construction.
library/​src/​test/​java/​io/​constructor/​core/​ConstructorIoProductInsightsAgentTrackingTest.kt Tests payloads, endpoints, errors, timestamps, and limits.
library/​src/​test/​java/​io/​constructor/​core/​ConstructorIoIntegrationTest.kt Adds live tests for all nine events.
library/​src/​main/​java/​io/​constructor/​data/​remote/​ConstructorApi.kt Declares nine Retrofit tracking endpoints.
library/​src/​main/​java/​io/​constructor/​data/​remote/​ApiPaths.kt Defines event URL constants.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentViewTimespanBody.kt Models serialized timestamp pairs.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentViewTimespan.kt Models epoch-millisecond view intervals.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentViewsRequestBody.kt Defines the multiple-interval view payload.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentViewRequestBody.kt Defines the view payload.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentResultClickRequestBody.kt Defines the recommended-item click payload.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentQuestionRequestBody.kt Shares payload fields for question events.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentQuestion.kt Models displayed questions and optional topics.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentItemRequestBody.kt Shares focus and out-of-view payload fields.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentFollowUpQuestion.kt Defines follow-up question serialization.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentFeedbackLabel.kt Defines thumbs-up and thumbs-down values.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentAnswerViewRequestBody.kt Defines answer-view text and recommendation fields.
library/​src/​main/​java/​io/​constructor/​data/​model/​tracking/​ProductInsightsAgentAnswerFeedbackRequestBody.kt Defines the answer-feedback payload.
library/​src/​main/​java/​io/​constructor/​data/​DataManager.kt Forwards tracking requests to the API.
library/​src/​main/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentViewTrackingData.kt Adds the view request builder.
library/​src/​main/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentViewsTrackingData.kt Adds the view-timespan request builder.
library/​src/​main/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentTrackingData.kt Defines the shared tracking interface.
library/​src/​main/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentResultClickTrackingData.kt Adds the result-click request builder.
library/​src/​main/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentQuestionSubmitTrackingData.kt Adds the question-submit request builder.
library/​src/​main/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentQuestionClickTrackingData.kt Adds the question-click request builder.
library/​src/​main/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentOutOfViewTrackingData.kt Adds the out-of-view request builder.
library/​src/​main/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentFocusTrackingData.kt Adds the focus request builder.
library/​src/​main/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentAnswerViewTrackingData.kt Adds the answer-view request builder.
library/​src/​main/​java/​io/​constructor/​data/​builder/​ProductInsightsAgentAnswerFeedbackTrackingData.kt Adds the answer-feedback request builder.
library/​src/​main/​java/​io/​constructor/​core/​ConstructorIo.kt Implements tracking methods and payload preparation.

Review effort: Balanced

Comment thread library/src/main/java/io/constructor/core/ConstructorIo.kt
@Mudaafi
Mudaafi requested a review from pedro-lb October 26, 2026 14:18
pedro-lb
pedro-lb previously approved these changes Oct 26, 2026

@pedro-lb pedro-lb 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.

great work @Mudaafi! 🔥

nothing blocking, just a few small q's before we proceed, but flagging as approved.

constructor-claude-bedrock[bot]

This comment was marked as outdated.

The API rejects answer_text over 2000 Unicode code points with a 400,
which drops the whole event silently. Truncate client-side, as items and
followUpQuestions are already capped, cutting on a code point boundary so
a surrogate pair is never split.

@constructor-claude-bedrock constructor-claude-bedrock Bot 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.

Code Review

This PR adds 9 Product Insights Agent (PIA) tracking events following the same builder pattern established in #173, with comprehensive test coverage (43 mock + 9 integration tests). The implementation is well-structured and consistent.

Inline comments: 6 discussions added

Overall Assessment: ⚠️ Needs Work

Comment thread library/src/main/java/io/constructor/core/ConstructorIo.kt
Comment thread library/src/main/java/io/constructor/core/ConstructorIo.kt
Comment thread library/src/main/java/io/constructor/core/ConstructorIo.kt

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants