Skip to content

Commit

Permalink
Merge pull request #13654 from woocommerce/issue/13629-jetpack-connec…
Browse files Browse the repository at this point in the history
…tion-api

[Jetpack Setup] Add networking layer for the Connection API
  • Loading branch information
hichamboushaba authored Mar 7, 2025
2 parents 05eb2e6 + 3c2b625 commit 8b5453a
Show file tree
Hide file tree
Showing 11 changed files with 291 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ class FetchJetpackStatus @Inject constructor(

@Suppress("ReturnCount", "NestedBlockDepth")
suspend operator fun invoke(): Result<JetpackStatusFetchResponse> {
return jetpackStore.fetchJetpackUser(selectedSite.get(), useApplicationPasswords = true).let { userResult ->
return jetpackStore.fetchJetpackConnectionData(
site = selectedSite.get(),
useApplicationPasswords = true
).let { userResult ->
when {
userResult.error?.errorCode == FORBIDDEN_CODE -> {
Result.success(JetpackStatusFetchResponse.ConnectionForbidden)
Expand Down Expand Up @@ -66,6 +69,7 @@ class FetchJetpackStatus @Inject constructor(
pluginResult.isError -> {
return Result.failure(OnChangedException(pluginResult.error))
}

else -> {
pluginResult.model!!.any { it.slug == JETPACK_SLUG && it.isActive }
}
Expand All @@ -76,8 +80,8 @@ class FetchJetpackStatus @Inject constructor(
JetpackStatusFetchResponse.Success(
JetpackStatus(
isJetpackInstalled = isJetpackInstalled,
isJetpackConnected = userResult.data!!.isConnected,
wpComEmail = userResult.data!!.wpcomEmail.orNullIfEmpty()
isJetpackConnected = userResult.data!!.currentUser.isConnected,
wpComEmail = userResult.data!!.currentUser.wpcomEmail.orNullIfEmpty()
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ class AccountMismatchRepository @Inject constructor(
}

private suspend fun fetchJetpackUser(site: SiteModel): Result<JetpackUser?> {
return jetpackStore.fetchJetpackUser(site, useApplicationPasswords = false).let {
return jetpackStore.fetchJetpackConnectionData(site, useApplicationPasswords = false).let {
if (it.isError) {
Result.failure(OnChangedException(it.error, it.error.message))
} else {
Result.success(it.data)
Result.success(it.data?.currentUser)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,17 @@ class JetpackActivationRepository @Inject constructor(
useApplicationPasswords: Boolean
): Result<String> = runWithRetry {
WooLog.d(WooLog.T.LOGIN, "Fetching email of Jetpack User")
val result = jetpackStore.fetchJetpackUser(site = site, useApplicationPasswords = useApplicationPasswords)
val result = jetpackStore.fetchJetpackConnectionData(
site = site,
useApplicationPasswords = useApplicationPasswords
)
return@runWithRetry when {
result.isError -> {
WooLog.w(WooLog.T.LOGIN, "Fetching Jetpack User failed error: $result.error.message")
Result.failure(OnChangedException(result.error, result.error.message))
}

result.data?.wpcomEmail.isNullOrEmpty() -> {
result.data?.currentUser?.wpcomEmail.isNullOrEmpty() -> {
analyticsTrackerWrapper.track(
stat = AnalyticsEvent.LOGIN_JETPACK_SETUP_CANNOT_FIND_WPCOM_USER
)
Expand All @@ -86,7 +89,7 @@ class JetpackActivationRepository @Inject constructor(

else -> {
WooLog.d(WooLog.T.LOGIN, "Jetpack User fetched successfully")
Result.success(result.data!!.wpcomEmail)
Result.success(result.data!!.currentUser.wpcomEmail)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.model.jetpack.JetpackConnectionData
import org.wordpress.android.fluxc.model.jetpack.JetpackUser
import org.wordpress.android.fluxc.store.JetpackStore
import org.wordpress.android.fluxc.store.SiteStore
Expand All @@ -33,8 +34,8 @@ class AccountMismatchRepositoryTest : BaseUnitTest() {

@Test
fun `given a non-connected Jetpack Account, when fetching status, then return correct status`() = testBlocking {
whenever(jetpackStore.fetchJetpackUser(any(), eq(false)))
.thenReturn(JetpackStore.JetpackResult(createJetpackUser(isConnected = false)))
whenever(jetpackStore.fetchJetpackConnectionData(any(), eq(false)))
.thenReturn(JetpackStore.JetpackResult(createJetpackConnectionData(isConnected = false)))

val result = sut.checkJetpackConnection(
siteUrl = "https://example.com",
Expand All @@ -47,7 +48,7 @@ class AccountMismatchRepositoryTest : BaseUnitTest() {

@Test
fun `given a null jetpack user, when fetching connection status, then assume non-connected`() = testBlocking {
whenever(jetpackStore.fetchJetpackUser(any(), eq(false)))
whenever(jetpackStore.fetchJetpackConnectionData(any(), eq(false)))
.thenReturn(JetpackStore.JetpackResult(null))

val result = sut.checkJetpackConnection(
Expand All @@ -61,8 +62,8 @@ class AccountMismatchRepositoryTest : BaseUnitTest() {

@Test
fun `given a connected Jetpack Account, when fetching status, then return correct status`() = testBlocking {
whenever(jetpackStore.fetchJetpackUser(any(), eq(false)))
.thenReturn(JetpackStore.JetpackResult(createJetpackUser(isConnected = true, wpcomEmail = "email")))
whenever(jetpackStore.fetchJetpackConnectionData(any(), eq(false)))
.thenReturn(JetpackStore.JetpackResult(createJetpackConnectionData(isConnected = true, wpcomEmail = "email")))

val result = sut.checkJetpackConnection(
siteUrl = "https://example.com",
Expand All @@ -76,8 +77,8 @@ class AccountMismatchRepositoryTest : BaseUnitTest() {

@Test
fun `given a correctly connected Jetpack account, when fetching email, then return it`() = testBlocking {
whenever(jetpackStore.fetchJetpackUser(any(), eq(false)))
.thenReturn(JetpackStore.JetpackResult(createJetpackUser(isConnected = true, wpcomEmail = "email")))
whenever(jetpackStore.fetchJetpackConnectionData(any(), eq(false)))
.thenReturn(JetpackStore.JetpackResult(createJetpackConnectionData(isConnected = true, wpcomEmail = "email")))

val result = sut.fetchJetpackConnectedEmail(SiteModel())

Expand All @@ -86,23 +87,28 @@ class AccountMismatchRepositoryTest : BaseUnitTest() {

@Test
fun `given an issue with Jetpack account, when fetching email, then return error`() = testBlocking {
whenever(jetpackStore.fetchJetpackUser(any(), eq(false)))
.thenReturn(JetpackStore.JetpackResult(createJetpackUser(isConnected = true, wpcomEmail = "")))
whenever(jetpackStore.fetchJetpackConnectionData(any(), eq(false)))
.thenReturn(JetpackStore.JetpackResult(createJetpackConnectionData(isConnected = true, wpcomEmail = "")))

val result = sut.fetchJetpackConnectedEmail(SiteModel())

assertThat(result.isFailure).isTrue()
}

private fun createJetpackUser(
private fun createJetpackConnectionData(
isConnected: Boolean = false,
wpcomEmail: String = ""
) = JetpackUser(
isConnected = isConnected,
wpcomEmail = wpcomEmail,
isMaster = false,
username = "username",
wpcomId = 1L,
wpcomUsername = "wpcomUsername"
) = JetpackConnectionData(
currentUser = JetpackUser(
isConnected = isConnected,
wpcomEmail = wpcomEmail,
isMaster = false,
username = "username",
wpcomId = 1L,
wpcomUsername = "wpcomUsername"
),
isSiteRegistered = null,
blogId = 1L,
connectionOwner = null
)
}
4 changes: 3 additions & 1 deletion libs/fluxc-processor/src/main/resources/jp-api-endpoints.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/module/stats/active
/connection/url
/connection/data
/jitm
/connection/register
/remote_provision
/jitm
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,6 @@

/jetpack-ai-transcription
/jetpack-ai-query
/sites/$site/jetpack-ai/ai-assistant-feature
/sites/$site/jetpack-ai/ai-assistant-feature

/sites/$site/jetpack-remote-connect-user
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package org.wordpress.android.fluxc.store

import org.junit.Ignore
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.network.rest.wpapi.jetpack.JetpackConnectionProvisionResponse
import org.wordpress.android.fluxc.network.rest.wpapi.jetpack.JetpackWPAPIRestClient
import org.wordpress.android.fluxc.tools.initCoroutineEngine
import kotlin.test.Test

@Suppress("unused")
@RunWith(MockitoJUnitRunner::class)
class JetpackStoreTest {
private val jetpackWPAPIRestClient: JetpackWPAPIRestClient = mock()
private val site: SiteModel = SiteModel()
Expand All @@ -18,9 +24,37 @@ class JetpackStoreTest {
)

@Test
@Ignore("Empty test to keep JUnit happy, we'll add real tests later")
@Suppress("EmptyFunctionBlock")
fun `empty test to keep JUnit happy`() {
fun `when registerSite is called, then call jetpackWPAPIRestClient`() {
runBlocking {
whenever(jetpackWPAPIRestClient.registerSite(site, useApplicationPasswords = true))
.thenReturn(JetpackWPAPIRestClient.JetpackWPAPIPayload(1L))

val result = jetpackStore.registerSite(site, useApplicationPasswords = true)

verify(jetpackWPAPIRestClient).registerSite(site, useApplicationPasswords = true)
assertThat(result).isEqualTo(JetpackStore.JetpackResult(1L))
}
}

@Test
fun `when connectJetpackAccount is called, then call jetpackWPAPIRestClient`() {
runBlocking {
val blogId = 1L
val provisionResponse = JetpackConnectionProvisionResponse(
userId = 1L,
secret = "secret",
scope = "scope"
)
whenever(jetpackWPAPIRestClient.provisionConnection(site, useApplicationPasswords = true))
.thenReturn(JetpackWPAPIRestClient.JetpackWPAPIPayload(provisionResponse))
whenever(jetpackWPAPIRestClient.connectJetpackAccount(site, blogId, provisionResponse))
.thenReturn(JetpackWPAPIRestClient.JetpackWPAPIPayload(Unit))

val result = jetpackStore.connectJetpackAccount(site, blogId = blogId, useApplicationPasswords = true)

verify(jetpackWPAPIRestClient).provisionConnection(site, useApplicationPasswords = true)
verify(jetpackWPAPIRestClient).connectJetpackAccount(site, blogId, provisionResponse)
assertThat(result).isEqualTo(JetpackStore.JetpackResult(Unit))
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package org.wordpress.android.fluxc.model.jetpack


data class JetpackConnectionData(
val currentUser: JetpackUser,
val blogId: Long?,
val isSiteRegistered: Boolean?,
val connectionOwner: String?
)

data class JetpackUser(
val isConnected: Boolean,
val isMaster: Boolean,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package org.wordpress.android.fluxc.network.rest.wpapi.jetpack

import com.google.gson.JsonPrimitive
import com.google.gson.annotations.SerializedName

data class JetpackConnectionDataResponse(
val currentUser: CurrentUser
@SerializedName("isRegistered") val isRegistered: Boolean?,
@SerializedName("currentUser") val currentUser: CurrentUser,
@SerializedName("connectionOwner") val connectionOwner: String?
)

data class CurrentUser(
@SerializedName("isConnected") val isConnected: Boolean?,
@SerializedName("isMaster") val isMaster: Boolean?,
@SerializedName("username") val username: String?,
@SerializedName("blogId") val blogId: JsonPrimitive?,
@SerializedName("wpcomUser") val wpcomUser: WpcomUser?,
@SerializedName("gravatar") val gravatar: String?,
@SerializedName("permissions") val permissions: Map<String, Boolean>?
Expand All @@ -25,3 +29,13 @@ data class WpcomUser(
@SerializedName("jetpack_connect") val jetpackConnect: String?,
@SerializedName("avatar") val avatar: String?
)

data class JetpackConnectionRegisterResponse(
val authorizeUrl: String
)

data class JetpackConnectionProvisionResponse(
@SerializedName("user_id") val userId: Long,
val scope: String,
val secret: String,
)
Loading

0 comments on commit 8b5453a

Please sign in to comment.