Skip to content

Commit

Permalink
For mozilla-mobile#12432 - Pocket sponsored stories api updates
Browse files Browse the repository at this point in the history
- Enforce the api version being an int instead of a string
- Enforce automatically choosing to which endpoint to make the requests,
development or production.
  • Loading branch information
Mugurell committed Jul 5, 2022
1 parent 6e2b017 commit b7103d9
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import mozilla.components.concept.fetch.Request.Body
import mozilla.components.concept.fetch.Request.Method
import mozilla.components.concept.fetch.Response
import mozilla.components.concept.fetch.isSuccess
import mozilla.components.service.pocket.BuildConfig
import mozilla.components.service.pocket.ext.fetchBodyOrNull
import mozilla.components.service.pocket.logger
import mozilla.components.service.pocket.spocs.api.SpocsEndpointRaw.Companion.newInstance
Expand All @@ -21,11 +22,12 @@ import org.json.JSONObject
import java.io.IOException
import java.util.UUID

private const val SPOCS_ENDPOINT_BASE_URL = "https://spocs.getpocket.dev/"
private const val SPOCS_ENDPOINT_DEV_BASE_URL = "https://spocs.getpocket.dev/"
private const val SPOCS_ENDPOINT_PROD_BASE_URL = "https://spocs.getpocket.com/"
private const val SPOCS_ENDPOINT_DOWNLOAD_SPOCS_PATH = "spocs"
private const val SPOCS_ENDPOINT_DELETE_PROFILE_PATH = "user"
private const val SPOCS_PROXY_VERSION_KEY = "version"
private const val SPOCS_PROXY_VERSION_VALUE = "2"
private const val SPOCS_PROXY_VERSION_VALUE = 2
private const val SPOCS_PROXY_PROFILE_KEY = "pocket_id"
private const val SPOCS_PROXY_APP_KEY = "consumer_key"

Expand All @@ -48,7 +50,7 @@ internal class SpocsEndpointRaw internal constructor(
@WorkerThread
fun getSponsoredStories(): String? {
val request = Request(
url = SPOCS_ENDPOINT_BASE_URL + SPOCS_ENDPOINT_DOWNLOAD_SPOCS_PATH,
url = baseUrl + SPOCS_ENDPOINT_DOWNLOAD_SPOCS_PATH,
method = Method.POST,
headers = getRequestHeaders(),
body = getDownloadStoriesRequestBody()
Expand All @@ -64,7 +66,7 @@ internal class SpocsEndpointRaw internal constructor(
@WorkerThread
fun deleteProfile(): Boolean {
val request = Request(
url = SPOCS_ENDPOINT_BASE_URL + SPOCS_ENDPOINT_DELETE_PROFILE_PATH,
url = baseUrl + SPOCS_ENDPOINT_DELETE_PROFILE_PATH,
method = Method.DELETE,
headers = getRequestHeaders(),
body = getDeleteProfileRequestBody()
Expand Down Expand Up @@ -114,5 +116,16 @@ internal class SpocsEndpointRaw internal constructor(
fun newInstance(client: Client, profileId: UUID, appId: String): SpocsEndpointRaw {
return SpocsEndpointRaw(client, profileId, appId)
}

/**
* Get the base url for sponsored stories specific to development or production.
*/
@VisibleForTesting
internal val baseUrl
get() = if (BuildConfig.DEBUG) {
SPOCS_ENDPOINT_DEV_BASE_URL
} else {
SPOCS_ENDPOINT_PROD_BASE_URL
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ package mozilla.components.service.pocket.spocs.api

import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.concept.fetch.Client
import mozilla.components.concept.fetch.Request
import mozilla.components.concept.fetch.Response
import mozilla.components.service.pocket.BuildConfig
import mozilla.components.service.pocket.helpers.MockResponses
import mozilla.components.service.pocket.helpers.assertClassVisibility
import mozilla.components.service.pocket.helpers.assertRequestParams
Expand All @@ -16,6 +18,7 @@ import mozilla.components.service.pocket.stories.api.PocketEndpointRaw
import mozilla.components.service.pocket.stories.api.PocketEndpointRaw.Companion
import mozilla.components.support.test.any
import mozilla.components.support.test.mock
import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
Expand All @@ -26,6 +29,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.doThrow
import org.robolectric.util.ReflectionHelpers
import java.io.IOException
import java.util.UUID
import kotlin.reflect.KVisibility
Expand Down Expand Up @@ -61,7 +65,8 @@ class SpocsEndpointRawTest {
}

@Test
fun `WHEN requesting spocs THEN the pocket proxy url is used`() {
fun `GIVEN a debug build WHEN requesting spocs THEN the appropriate pocket proxy url is used`() {
ReflectionHelpers.setStaticField(BuildConfig::class.java, "DEBUG", true)
val expectedUrl = "https://spocs.getpocket.dev/spocs"

assertRequestParams(
Expand All @@ -71,6 +76,50 @@ class SpocsEndpointRawTest {
},
assertParams = { request ->
assertEquals(expectedUrl, request.url)
assertEquals(Request.Method.POST, request.method)

val requestBody = JSONObject(
request.body!!.useStream {
it.bufferedReader().readText()
}
)
assertEquals(2, requestBody["version"])
assertEquals(appId, requestBody["consumer_key"])
assertEquals(profileId.toString(), requestBody["pocket_id"])

request.headers!!.first {
it.name.equals("Content-Type", true)
}.value.contains("application/json", true)
}
)
}

@Test
fun `GIVEN a release build WHEN requesting spocs THEN the appropriate pocket proxy url is used`() {
ReflectionHelpers.setStaticField(BuildConfig::class.java, "DEBUG", false)
val expectedUrl = "https://spocs.getpocket.com/spocs"

assertRequestParams(
client,
makeRequest = {
endpoint.getSponsoredStories()
},
assertParams = { request ->
assertEquals(expectedUrl, request.url)
assertEquals(Request.Method.POST, request.method)

val requestBody = JSONObject(
request.body!!.useStream {
it.bufferedReader().readText()
}
)
assertEquals(2, requestBody["version"])
assertEquals(appId, requestBody["consumer_key"])
assertEquals(profileId.toString(), requestBody["pocket_id"])

request.headers!!.first {
it.name.equals("Content-Type", true)
}.value.contains("application/json", true)
}
)
}
Expand All @@ -96,6 +145,40 @@ class SpocsEndpointRawTest {
assertNull(endpoint.getSponsoredStories())
}

@Test
fun `GIVEN a debug build WHEN requesting profile deletion THEN the appropriate pocket proxy url is used`() {
ReflectionHelpers.setStaticField(BuildConfig::class.java, "DEBUG", true)
val expectedUrl = "https://spocs.getpocket.dev/user"

assertRequestParams(
client,
makeRequest = {
endpoint.deleteProfile()
},
assertParams = { request ->
assertEquals(expectedUrl, request.url)
assertEquals(Request.Method.DELETE, request.method)
}
)
}

@Test
fun `GIVEN a release build WHEN requesting profile deletion THEN the appropriate pocket proxy url is used`() {
ReflectionHelpers.setStaticField(BuildConfig::class.java, "DEBUG", false)
val expectedUrl = "https://spocs.getpocket.com/user"

assertRequestParams(
client,
makeRequest = {
endpoint.deleteProfile()
},
assertParams = { request ->
assertEquals(expectedUrl, request.url)
assertEquals(Request.Method.DELETE, request.method)
}
)
}

@Test
fun `WHEN requesting profile deletion and the client throws an IOException THEN false is returned`() {
doThrow(IOException::class.java).`when`(client).fetch(any())
Expand Down Expand Up @@ -154,4 +237,20 @@ class SpocsEndpointRawTest {

assertSame(client, result.client)
}

@Test
fun `GIVEN a debug build WHEN querying the base url THEN use the development endpoint`() {
ReflectionHelpers.setStaticField(BuildConfig::class.java, "DEBUG", true)
val expectedUrl = "https://spocs.getpocket.dev/"

assertEquals(expectedUrl, SpocsEndpointRaw.baseUrl)
}

@Test
fun `GIVEN a release build WHEN querying the base url THEN use the production endpoint`() {
ReflectionHelpers.setStaticField(BuildConfig::class.java, "DEBUG", false)
val expectedUrl = "https://spocs.getpocket.com/"

assertEquals(expectedUrl, SpocsEndpointRaw.baseUrl)
}
}
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ permalink: /changelog/
* [Gecko](/~https://github.com/mozilla-mobile/android-components/blob/main/buildSrc/src/main/java/Gecko.kt)
* [Configuration](/~https://github.com/mozilla-mobile/android-components/blob/main/.config.yml)

* **service-pocket**
* Fix recent breakage of the sponsored stories feature and allow to dynamically make requests to either the development server or the production one. [Issue #12432](/~https://github.com/mozilla-mobile/android-components/issues/12432)

* **feature-top-sites**
* Replaced `frecencyConfig` from `TopSitesConfig` with `TopSitesFrecencyConfig`, which specifies the `FrecencyTresholdOption` and the frecency filter, an optional function used to filter the top frecent sites. [#12384] (/~https://github.com/mozilla-mobile/android-components/issues/12384)

Expand Down

0 comments on commit b7103d9

Please sign in to comment.