Skip to content

[REM-3440] Viewable impressions tracking#167

Merged
Alexey-Pavlov merged 4 commits into
masterfrom
REM-3440/viewable-impressions-tracking
Jul 1, 2026
Merged

[REM-3440] Viewable impressions tracking#167
Alexey-Pavlov merged 4 commits into
masterfrom
REM-3440/viewable-impressions-tracking

Conversation

@ZSnake

@ZSnake ZSnake commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Viewable impressions tracking

For context, we're introducing viewable impressions tracking for Sponsored listings to get a better outlook on wether a sponsored product was actually viewed by the end user. This PR will introduce trackResultsImpressionView to track viewable impressions

References

Copilot AI review requested due to automatic review settings June 11, 2026 17:02
@ZSnake ZSnake requested a review from a team as a code owner June 11, 2026 17:02

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.

Pull request overview

This PR introduces a new public tracking API to report “viewable” (MRC-style) sponsored results impressions from the Android SDK, wiring it through the API layer and adding unit test coverage plus README usage docs.

Changes:

  • Added ConstructorIo.trackResultsImpressionView(...) (and internal implementation) to send the new impression-view tracking event.
  • Added new request/DTO models (ResultsImpressionViewRequestBody, ResultsImpressionItem) and a new API path + Retrofit endpoint wiring.
  • Added a dedicated unit test suite and documented usage in README.md.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
README.md Documents how to call trackResultsImpressionView with ResultsImpressionItem instances.
library/src/test/java/io/constructor/core/ConstructorIoResultsImpressionViewTrackingTest.kt Adds unit tests validating request path, payload fields, and error/timeout behavior for the new event.
library/src/main/java/io/constructor/data/remote/ConstructorApi.kt Adds Retrofit API method for the results impression view endpoint.
library/src/main/java/io/constructor/data/remote/ApiPaths.kt Introduces URL_RESULTS_IMPRESSION_VIEW_EVENT constant.
library/src/main/java/io/constructor/data/model/tracking/ResultsImpressionViewRequestBody.kt Adds Moshi-serializable request body model for the new tracking call.
library/src/main/java/io/constructor/data/model/common/ResultsImpressionItem.kt Adds Moshi-serializable item payload model used by the new tracking event.
library/src/main/java/io/constructor/data/DataManager.kt Wires DataManager method to call the new Retrofit API endpoint.
library/src/main/java/io/constructor/core/ConstructorIo.kt Adds public trackResultsImpressionView API and internal request construction + dispatch.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

constructor-claude-bedrock[bot]

This comment was marked as outdated.

constructor-claude-bedrock[bot]

This comment was marked as outdated.

@Alexey-Pavlov Alexey-Pavlov 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.

Thanks for working on it! Left a few comments. Could you please take a look when you get a chance?

Comment thread library/src/main/java/io/constructor/core/ConstructorIo.kt
Comment thread library/src/main/java/io/constructor/core/ConstructorIo.kt
ZSnake added 2 commits June 30, 2026 15:38
…d KDoc to trackResultsImpressionView, trackMediaImpressionClick and trackMediaImpressionView
@ZSnake ZSnake requested a review from Alexey-Pavlov June 30, 2026 14:00

@Alexey-Pavlov Alexey-Pavlov 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.

Thanks for the changes!

@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 introduces trackResultsImpressionView for viewable impression tracking of sponsored listings, following established patterns in the codebase closely. The implementation is clean and the test coverage is solid.

Inline comments: 6 discussions added

Overall Assessment: ⚠️ Needs Work

@Json(name = "s") val s: Int,
@Json(name = "key") val key: String,
@Json(name = "ui") val ui: String?,
@Json(name = "us") val us: List<String?>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important Issue: The us field type List<String?> allows nullable elements in the list, which is consistent with other request body classes in the codebase (e.g., MediaImpressionRequestBody, GenericResultClickRequestBody). However, _dt is typed as Long? but System.currentTimeMillis() is always passed — it is never null at the call site. Consider making it Long (non-nullable) to better express the intent, matching what's guaranteed by trackResultsImpressionViewInternal. (The same issue exists in MediaImpressionRequestBody, but introducing nullable where it isn't needed here would continue the pattern inconsistency.)

return constructorApi.trackQuizConversion(quizConversionRequestBody, params.toMap())
}

fun trackResultsImpressionView(resultsImpressionViewRequestBody: ResultsImpressionViewRequestBody, params: Array<Pair<String, String>> = arrayOf()): Completable {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The params parameter (Array<Pair<String, String>> = arrayOf()) is never used by the caller — trackResultsImpressionViewInternal in ConstructorIo.kt invokes dataManager.trackResultsImpressionView(requestBody) with no second argument, and there is no mechanism to pass query params through the public API. This dead parameter adds noise and could confuse future maintainers. Either remove it (matching the actual usage), or if query param extensibility is intentional, document it clearly.

* @param filterValue the value of the filter applied
* @param analyticsTags Additional analytics tags to pass
*/
fun trackResultsImpressionView(items: List<ResultsImpressionItem>, searchTerm: String? = null, filterName: String? = null, filterValue: String? = null, analyticsTags: Map<String, String>? = null) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important Issue: The method signature allows filterName and filterValue to be passed independently (one without the other), but they are semantically coupled — a filter name without a value (or vice versa) is likely invalid or meaningless. Either:

  1. Accept a Pair<String, String>? or a dedicated FilterPair data class to enforce they are always provided together, or
  2. Add a runtime validation (similar to the items.isEmpty() guard) and document that both must be provided together.

This same coupling issue exists on the request body itself.

* @param analyticsTags Additional analytics tags to pass
*/
fun trackResultsImpressionView(items: List<ResultsImpressionItem>, searchTerm: String? = null, filterName: String? = null, filterValue: String? = null, analyticsTags: Map<String, String>? = null) {
if (items.isEmpty()) return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Silently returning on empty items in the public method is good for safety, but it could mask caller bugs — a caller may accidentally pass an empty list and never know the event was dropped. Consider adding a log warning (using the existing e(...) or w(...) log pattern used elsewhere in this file) before returning, so it's detectable during development:

if (items.isEmpty()) {
    w("trackResultsImpressionView called with empty items list, skipping")
    return
}

assertEquals("true", requestBody["beacon"])
assertEquals("wacko-the-guid", requestBody["i"])
assertEquals("67", requestBody["s"])
assert(requestBody["items"]!!.contains("item_id:item-1"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The item assertions use contains("item_id:item-1") which is a substring check on the raw serialized string and could produce false positives (e.g., an item with id "item-1-extra" would also match "item_id:item-1"). This is a limitation of the shared getRequestBody test helper. Consider adding more specific assertions or a comment explaining the testing limitation. Also, the test for trackResultsImpressionView (the basic case) does not verify the key ("copper-key") or ui ("player-three") fields that are verified in similar tests in the codebase — adding those would improve confidence in the full request body construction.

}

@Test
fun trackResultsImpressionView() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: There is no test for the empty-items guard behavior. A test like trackResultsImpressionViewWithEmptyItems that verifies no HTTP request is made when items is empty would close this gap and document the intended behavior:

@Test
fun trackResultsImpressionViewWithEmptyItems() {
    val observer = ConstructorIo.trackResultsImpressionViewInternal(emptyList()).test()
    observer.assertComplete()
    val request = mockServer.takeRequest(1, TimeUnit.SECONDS)
    assertNull(request) // no request should be made
}

@Alexey-Pavlov Alexey-Pavlov merged commit 38963b0 into master Jul 1, 2026
1 check passed
@Alexey-Pavlov Alexey-Pavlov deleted the REM-3440/viewable-impressions-tracking branch July 1, 2026 12:57
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