---
# System prepended metadata

title: Prudsys Recommendation click tracking

---

# Prudsys Recommendation click tracking

### Click tracking endpoint: 

https://{{host}}/rde_server/res/{{cid}}/plugins/exec/prudsys/prudsys/event/click?pid={{pid}}&sid={{sid}}&trackingtoken={{trackingToken}}&userid={{userid}}&tracking={{tracking}}

#### Host: https://hse-shop.personalization.air.prudsys.com

#### Example: https://hseshop.personalization.air.prudsys.com/rde_server/res/hseAT/plugins/exec/prudsys/prudsys/event/clickpid=447811191&sid=test&trackingtoken=Produktdetailseite%7Carea1%7CA%7CStandard%7COrdered-Together%7COTG&userid=test&tracking=true

### The parameters used are : 

* **`cid`** : country id and it’s should be equal to one of the three values: `hseDE`, `hseAT` or `hseCH`
* **`pid`** : id of the product.
* **`sid`** : it represents session id, unique generated value for each device session . 
* **`trackingToken`** : a token related to each product provided by Prudsys engine while fetching recommendations, this parameter could contain some special characters so it should encode in request
* **`userid`** : id of the user, it is optional in case there is a user assigned to the session 
* **tracking** : we need explicit permission to track person data and provide recommendations on this data. The permission is granted with the request parameter: tracking=<TRACKING-ALLOWED>. This flag controls tracking of requests per session depending on the consent decision selected at startup. 

**Every click on product from a recommended list (in Basket and PDP) should be tracked in Prudsys engine by calling this endpoint with described parameters**


---

## Steps 
 
**1. We need the tracking token from the recommendations in order to be able to track the click of products by SKU**

* Create `Recommendation` model

```
data class Recommendation(
    private val sku: Long,
    private val trackingToken: String?,
)
```

* Change the return value of `RecommendationsResponseBody` class to `List<Recommendation>`

```
data class RecommendationsResponseBody(@SerializedName("recommendations") private val recommendations: Recommendations) {
    val items: List<Recommendation>
        get() = with(recommendations.area.items) {
            return this.map {
                Recommendation(
                    pdpSku = it.data.UID,
                    pdpTrackingToken = it.attributes?.trackingtoken,
                )
            }
        }
}
```
* Create the `RecommendationsResponseBodyToRecommendationsMapper` mapper from `RecommendationsResponseBody` to `Recommendations` (no tests are necessary for this mapper, it is only unwrapping the model there is nothing to test)
    
```
class RecommendationsResponseBodyToRecommendationsMapper(
    defaultDispatcher: CoroutineDispatcher
) : NewSuspendingMapper<RecommendationsResponseBody, List<Recommnedation>> {
    // ...
}
```
* Change items type in the rest of the classes and functions.
    
**2. We need to create a new function to be able to track the click of a recommended product** 
    
* Add `clickTrackingServiceCreator`
    

```
class RecommendationServiceProvider(
    override val gson: Gson,
    override val responseToLogEntryMapper: ResponseToLogEntryMapper,
) : RemoteServiceProvider {
    val recommendationServiceCreator: () -> RecommendationService by lazyServiceCreator(
        RecommendationService::class
    )

    val clickTrackingServiceCreator: () -> ClickTrackingService by lazyServiceCreator(
        ClickTrackingService::class
    )
    
    ...
```

       
* Create a new function `trackRecommendationClick` in `RecommendationService` class
    
```
interface RecommendationService {
    @GET("/{{cid}}/plugins/exec/prudsys/prudsys/event/click?pid={{pid}}&sid={{sid}}&trackingtoken={{trackingToken}}&userid={{userid}}&tracking={{tracking}}")
    suspend fun trackRecommendationClick(
        @Path(COUNTRY_ID) countryId: String,
        @Query(PRODUCT_ID) productId: String,
        @Query(SESSION_ID) sessionId: String,
        @Path(TRACKING_TOKEN) trackingToken: String,
        @Query(USER_ID) userId: String?,
        @Query(IS_TRACKING_ENABLED) tracking: Boolean,
    ): Response

    companion object {
        const val COUNTRY_ID = "cid"
        const val PRODUCT_ID = "pid"
        const val SESSION_ID = "sid"
        const val TRACKING_TOKEN = "trackingToken"
        const val IS_TRACKING_ENABLED = "tracking"
        const val USER_ID = "userid"
    }
}
```
 
**3. We need to expose a new function to be able to track recommendation clicks**
    
    
* Add `trackRecommendationClick` function to `Tracking Repository`
* Add `RecommendationsServiceCreator` to the `TrackingRepositoryImpl` and declare it in the `RepositoryModule`
* Override `trackRecommendationClick` function
* Copy `getSelectedRecommendationsCountryCodeOrThrow()`, `getSessionIdOrThrow()`, `getCurrentSession()`, `isTrackingEnabled()` from `RecommendationRepositoryImpl`
    
```
private suspend fun trackRecommendationClick(
        recommendation: Recommendation,
    ) {
        clickTrackingServiceCreator().runCatching {
            trackRecommendationClick(
                countryIdentifier = getSelectedRecommendationsCountryCodeOrThrow(),
                productId = recommendation.pdpSku,
                sessionId = getSessionIdOrThrow(),
                trackingToken = recommendation.pdpTrackingToken,
                userid = sessionDataSource.getCurrentSession()?.customerId,
                tracking = isTrackingEnabled(),
            )
        }.getOrThrow()
    }
```

**4. We need to expose a new UseCase to be able to track PDP recommendation clicks**
 
* Modify the newInstance functions for: `ProductTilesSliderFragment`, `ProductTilesListFragment`, `OnAirProductTilesFragment` to pass a Boolean value to determine if the fragment is a recommendations fragment
* Add a lazyArgument `isFromRecommendations` to `ProductTilesFragment` and pass it to the ViewModel
* The `ProductTilesAction.SelectProduct` should have an `isFromRecommendations` parameter
* Create `TrackRecommendationClickUseCase` and call it with the `Recommendation` from a new side efffect `ProductTilesSideEffect.TrackRecommendationClick` only if `isFromRecommendations` is true
* Create tests for `TrackRecommendationClickUseCase`
* When the action `SelectRecommendation` event is dispatched from `ProductDetailViewModel` a side effect: `TrackRecommendationClick` should be triggered
    
```
class TrackRecommendationClickUseCase(
    private val recommendationRepository: RecommendationRepository,
    private val trackingRepository: TrackingRepository
) : SuspendingUseCase<TrackingProductItem, Unit> {

    override suspend fun execute(parameter: Recommendation) {
        // Use your imagination
    }
}
```
**5. We need to be able to track the product recommendations when we obtain them from Prudsys. Right now we are obtaining them but we are not linking the tracking token with any SKU. To avoid passing the tracking token to the UI layer that has no concern with this information we should persist it in the data layer and remove it when the app is re-started or the user session ends.**
    
* The `RecommendationEntity` is created
* The `RecommendationDao` is created and has the following functions:
    * Add recommendation (on conflict replace)
    * Clear recommendations
* Add the migration to create the new table in the `MigrationManager`
* Add a new function `clearRecommendations` in the `RecommendationRepository`
* Add a new use case: `ClearRecommendationsUseCase`
* Call the new UseCase in `SplashViewModel` in the function `initializeSynchronousProcesses`
    
We should create a new entity and DAO representing a table and its operations, respectively. The table should be cleared on app startup and filled whenever we obtain recommendations from the recommendations data source (Prudsys). We should also expose a function in the RecommendationsRepository to get a tracking token by SKU to use it in TrackRecommnedationClickUseCase