195 lines
7.3 KiB
Markdown
195 lines
7.3 KiB
Markdown
# Curated Recommendations Client
|
|
|
|
Fetches personalized content recommendations from the Merino Service. [Merino Curated Recommendations API Docs](https://merino.services.mozilla.com/docs#/default/curated_content_api_v1_curated_recommendations_post)
|
|
|
|
The API for the `CuratedRecommendationsClient` can be found in the Mozilla Rust components [Kotlin API Reference](https://mozilla.github.io/application-services/kotlin/kotlin-components-docs/mozilla.appservices.merino/index.html) and [Swift API Reference](https://mozilla.github.io/application-services/swift/Classes/CuratedRecommendationsClient.html).
|
|
|
|
|
|
## Prerequisites
|
|
|
|
Ensure that {doc}`viaduct` is initialized during application startup, as it is used for making network requests.
|
|
|
|
## Async
|
|
|
|
The Curated Recommendations API is synchronous, meaning calling it directly will block the current thread. To mitigate this, consumers should wrap the API in an async implementation.
|
|
|
|
## Importing the Client
|
|
|
|
:::{tab-set-code}
|
|
|
|
```kotlin
|
|
import mozilla.appservices.merino.curatedrecommendations.CuratedRecommendationsClient
|
|
import mozilla.appservices.merino.curatedrecommendations.CuratedRecommendationsRequest
|
|
import mozilla.appservices.merino.curatedrecommendations.CuratedRecommendationsResponse
|
|
import mozilla.appservices.merino.curatedrecommendations.CuratedRecommendationsError
|
|
```
|
|
|
|
|
|
```swift
|
|
import MozillaAppServices
|
|
```
|
|
:::
|
|
|
|
## Initializing the Curated Recommendations Client
|
|
|
|
The `CuratedRecommendationsClient` is initialized using a `CuratedRecommendationsConfig` object. This includes a required `userAgentHeader` and an optional `baseHost`. If `baseHost` is not provided, the client will default to the production host.
|
|
|
|
:::{tab-set-code}
|
|
```kotlin
|
|
|
|
val config = CuratedRecommendationsConfig(
|
|
baseHost = "https://merino.services.mozilla.com"
|
|
userAgentHeader = "Mozilla/5.0"
|
|
)
|
|
|
|
val client = CuratedRecommendationsClient(config)
|
|
|
|
```
|
|
|
|
```swift
|
|
|
|
let config = CuratedRecommendationsConfig(
|
|
baseHost: "https://merino.services.mozilla.com"
|
|
userAgentHeader: "Mozilla/5.0"
|
|
)
|
|
|
|
let client = try CuratedRecommendationsClient(config: config)
|
|
|
|
```
|
|
:::
|
|
|
|
|
|
## Fetching Curated Recommendations
|
|
|
|
The `getCuratedRecommendations()` method fetches recommendations based on the provided request parameters.
|
|
|
|
:::{tab-set-code}
|
|
```kotlin
|
|
|
|
val request = CuratedRecommendationsRequest(
|
|
locale = Locale.EN_US,
|
|
region = "US",
|
|
count = 4,
|
|
topics = listOf("business"),
|
|
feeds = listOf("sections")
|
|
)
|
|
|
|
try {
|
|
val response: CuratedRecommendationsResponse = client.getCuratedRecommendations(request)
|
|
println("Received recommendations: $response")
|
|
} catch (e: CuratedRecommendationsError) {
|
|
println("Error fetching recommendations: ${e.message}")
|
|
}
|
|
|
|
```
|
|
|
|
```swift
|
|
|
|
let request = CuratedRecommendationsRequest(
|
|
locale: Locale.en-US,
|
|
region: "US",
|
|
count: 4,
|
|
topics: ["business"],
|
|
feeds: ["sections"]
|
|
)
|
|
|
|
do {
|
|
let response = try client.getCuratedRecommendations(request: request)
|
|
print("Received recommendations: \(response)")
|
|
} catch {
|
|
print("Error fetching recommendations: \(error)")
|
|
}
|
|
|
|
```
|
|
:::
|
|
|
|
## Data Models
|
|
|
|
### Curated Recommendations Request Model
|
|
|
|
The `CuratedRecommendationsRequest` model defines the parameters required to request curated recommendations.
|
|
|
|
### Request Fields
|
|
| **Field** | **Type** | **Description** |
|
|
|-----------|---------|----------------|
|
|
| `locale` | `string` | The Firefox installed locale, e.g., `en`, `en-US`, `de-DE`. Determines the language of recommendations. |
|
|
| `region` | `string (optional)` | _(Optional)_ The country-level region, e.g., `US` or `IE`. Helps return more relevant recommendations. If not provided, it is extracted from `locale` if it contains two parts (e.g., `en-US`). |
|
|
| `count` | `integer (optional)` | _(Optional)_ The maximum number of recommendations to return. Defaults to `100`. |
|
|
| `topics` | `array<string> (optional)` | _(Optional)_ A list of preferred [curated topics](https://mozilla-hub.atlassian.net/wiki/x/LQDaMg). |
|
|
| `feeds` | `array<string> (optional)` | _(Optional)_ A list of additional data feeds. Accepted values: `"need_to_know"`, `"fakespot"`, and `"sections"`. |
|
|
| `sections` | `array<object> (optional)` | _(Optional)_ A list of section settings that the user follows or has blocked. |
|
|
| `experimentName` | `string (optional)` | _(Optional)_ The Nimbus New Tab experiment name that the user is enrolled in. Used to run backend experiments independently of Firefox releases. |
|
|
| `experimentBranch` | `string (optional)` | _(Optional)_ The branch name of the Nimbus experiment that the user is in. |
|
|
| `enableInterestPicker` | `boolean (optional, default: false)` | _(Optional, defaults to `false`)_ If `true`, the API response will include an `interestPicker` object with sections for interest bubbles. |
|
|
|
|
|
|
|
|
### Curated Recommendations Response Model
|
|
|
|
The `CuratedRecommendationsResponse` model defines the response format containing recommendations.
|
|
|
|
### Response Fields
|
|
| **Field** | **Type** | **Description** |
|
|
|-----------|---------|----------------|
|
|
| `recommendedAt` | `integer` | The timestamp (in milliseconds) indicating when the recommendations were generated. |
|
|
| `data` | `array<object>` | A list of curated recommendation items. |
|
|
| `feeds` | `object (optional)` | _(Optional)_ A structured set of multiple curated recommendation lists. |
|
|
| `interestPicker` | `object (optional)` | _(Optional)_ Returned if `enableInterestPicker` is `true` in the request. Specifies the display order (`receivedFeedRank`) and a list of sections (referenced by `sectionId`) for interest bubbles. The text in these bubbles should match the corresponding section title. |
|
|
|
|
|
|
## Error Handling
|
|
|
|
The Curated Recommendations component defines the following error hierarchy:
|
|
- **`CuratedRecommendationsApiError`**: Base error
|
|
- **`Network(reason: string)`**: Network error while making a request.
|
|
- **`Other(code: integer (optional), reason: string)`**: Generic error containing an HTTP status code and message.
|
|
|
|
|
|
### Handling Errors in Kotlin and Swift
|
|
|
|
:::{tab-set-code}
|
|
|
|
```kotlin
|
|
fun fetchCuratedRecommendations() {
|
|
try {
|
|
val response = client.getCuratedRecommendations(request)
|
|
} catch (e: CuratedRecommendationsError.Network) {
|
|
// Log and retry after 5 minutes
|
|
Log.w("Network error when fetching Curated Recommendations: ${e.reason}")
|
|
scheduleRetry(300)
|
|
} catch (e: CuratedRecommendationsError.Other) {
|
|
when (e.code) {
|
|
400 -> Log.e("Bad Request: ${e.reason}")
|
|
422 -> Log.e("Validation Error: ${e.reason}")
|
|
in 500..599 -> Log.e("Server Error: ${e.reason}")
|
|
else -> Log.e("Unexpected Error: ${e.reason}")
|
|
}
|
|
}
|
|
}
|
|
|
|
```
|
|
|
|
```swift
|
|
func fetchCuratedRecommendations() {
|
|
do {
|
|
let response = try client.getCuratedRecommendations(request)
|
|
} catch CuratedRecommendationsError.Network(let reason) {
|
|
// Log and retry after 5 minutes
|
|
print("Network error when fetching Curated Recommendations: \(reason)")
|
|
scheduleRetry(seconds: 300)
|
|
} catch CuratedRecommendationsError.Other(let code, let reason) {
|
|
switch code {
|
|
case 400:
|
|
print("Bad Request: \(reason)")
|
|
case 422:
|
|
print("Validation Error: \(reason)")
|
|
case 500...599:
|
|
print("Server Error: \(reason)")
|
|
default:
|
|
print("Unexpected Error: \(reason)")
|
|
}
|
|
}
|
|
}
|
|
|
|
```
|
|
:::
|