Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
-----
- [*] Woo POS: Remote Tap to Pay failures now explain what went wrong - phone not eligible for Tap to Pay, NFC turned off, or a payment service error - instead of a generic message or raw error text [https://github.com/woocommerce/woocommerce-android/pull/16384]
- [*] QR login now asks you to choose a store when your WordPress.com account has multiple sites instead of preselecting one [https://github.com/woocommerce/woocommerce-android/pull/16400]
- [*] Store login now shows a connection error when a site's REST API hides its namespaces instead of continuing into misleading setup screens [https://github.com/woocommerce/woocommerce-android/pull/16408]
- [Internal] Close analytics gaps in the Remote Tap to Pay flow: transport on the reader-ready event, cause chains in error descriptions, no stale card reader model, and a real session ended reason [https://github.com/woocommerce/woocommerce-android/pull/16368]
- [*] Tap to Pay no longer disappears from the Payments screen shortly after it appears [https://github.com/woocommerce/woocommerce-android/pull/16370]
- [*] Fixed a crash when pressing Back on the "You're already signed in" warning shown after opening a QR login link while signed in [https://github.com/woocommerce/woocommerce-android/pull/16347]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import com.android.volley.RequestQueue
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.wordpress.android.fluxc.Dispatcher
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError
import org.wordpress.android.fluxc.network.BaseRequest.GenericErrorType.INVALID_RESPONSE
import org.wordpress.android.fluxc.network.UserAgent
import org.wordpress.android.fluxc.network.discovery.DiscoveryUtils
import org.wordpress.android.fluxc.network.discovery.DiscoveryWPAPIRestClient
Expand Down Expand Up @@ -54,15 +56,21 @@ class SiteWPAPIRestClient @Inject constructor(
return when (result) {
is Success -> {
val response = result.data
if (response?.namespaces.isNullOrEmpty()) {
return SiteModel().apply {
error = BaseNetworkError(INVALID_RESPONSE)
}
}

SiteModel().apply {
name = response?.name
timezone = response?.gmtOffset
name = response.name
timezone = response.gmtOffset
origin = SiteModel.ORIGIN_WPAPI
hasWooCommerce = response?.namespaces?.any {
hasWooCommerce = response.namespaces.any {
it.startsWith(WOO_API_NAMESPACE_PREFIX)
} ?: false
}

applicationPasswordsAuthorizeUrl = response?.authentication?.applicationPasswords
applicationPasswordsAuthorizeUrl = response.authentication?.applicationPasswords
?.endpoints?.authorization
if (!applicationPasswordsAuthorizeUrl.isNullOrEmpty() &&
applicationPasswordsAuthorizeUrl.contains(APPLICATION_PASSWORDS_URL_SUFFIX)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.wordpress.android.fluxc.network.rest.wpapi.site

import com.android.volley.RequestQueue
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.robolectric.RobolectricTestRunner
import org.wordpress.android.fluxc.Dispatcher
import org.wordpress.android.fluxc.network.BaseRequest.GenericErrorType.INVALID_RESPONSE
import org.wordpress.android.fluxc.network.UserAgent
import org.wordpress.android.fluxc.network.discovery.DiscoveryWPAPIRestClient
import org.wordpress.android.fluxc.network.discovery.RootWPAPIRestResponse
import org.wordpress.android.fluxc.network.rest.wpapi.WPAPIGsonRequestBuilder
import org.wordpress.android.fluxc.network.rest.wpapi.WPAPIResponse.Success
import org.wordpress.android.fluxc.store.SiteStore.FetchWPAPISitePayload
import org.wordpress.android.fluxc.test

@Suppress("UnitTestNamingRule")
@RunWith(RobolectricTestRunner::class)
class SiteWPAPIRestClientTest {
private val wpapiGsonRequestBuilder: WPAPIGsonRequestBuilder = mock()
private val discoveryWPAPIRestClient: DiscoveryWPAPIRestClient = mock()
private val dispatcher: Dispatcher = mock()
private val requestQueue: RequestQueue = mock()
private val userAgent: UserAgent = mock()

private lateinit var restClient: SiteWPAPIRestClient

@Before
fun setUp() {
restClient = SiteWPAPIRestClient(
wpapiGsonRequestBuilder = wpapiGsonRequestBuilder,
discoveryWPAPIRestClient = discoveryWPAPIRestClient,
dispatcher = dispatcher,
requestQueue = requestQueue,
userAgent = userAgent
)
}

@Test
fun `given application passwords and empty namespaces, when fetching site, then return invalid response error`() =
test {
initResponse(
RootWPAPIRestResponse(
namespaces = emptyList(),
authentication = RootWPAPIRestResponse.Authentication(
applicationPasswords = RootWPAPIRestResponse.Authentication.ApplicationPasswords(
endpoints = RootWPAPIRestResponse.Authentication.ApplicationPasswords.Endpoints(
authorization = APPLICATION_PASSWORDS_URL
)
)
)
)
)

val site = restClient.fetchWPAPISite(FetchWPAPISitePayload(SITE_URL))

assertThat(site.isError).isTrue()
assertThat(site.error.type).isEqualTo(INVALID_RESPONSE)
assertThat(site.applicationPasswordsAuthorizeUrl).isNull()
}

@Test
fun `given missing namespaces, when fetching site, then return invalid response error`() = test {
initResponse(RootWPAPIRestResponse(namespaces = null))

val site = restClient.fetchWPAPISite(FetchWPAPISitePayload(SITE_URL))

assertThat(site.isError).isTrue()
assertThat(site.error.type).isEqualTo(INVALID_RESPONSE)
}

@Test
fun `given non-empty namespaces without WooCommerce, when fetching site, then return non-Woo site`() = test {
initResponse(RootWPAPIRestResponse(namespaces = listOf("wp/v2")))

val site = restClient.fetchWPAPISite(FetchWPAPISitePayload(SITE_URL))

assertThat(site.isError).isFalse()
assertThat(site.hasWooCommerce).isFalse()
}

private suspend fun initResponse(response: RootWPAPIRestResponse) {
whenever(discoveryWPAPIRestClient.discoverWPAPIBaseURL(SITE_URL)).thenReturn(WP_API_URL)
whenever(
wpapiGsonRequestBuilder.syncGetRequest(
restClient = restClient,
url = WP_API_URL,
clazz = RootWPAPIRestResponse::class.java,
params = mapOf("_fields" to FETCH_API_CALL_FIELDS)
)
).thenReturn(Success(response, emptyList()))
}

companion object {
private const val SITE_URL = "https://example.com"
private const val WP_API_URL = "$SITE_URL/wp-json/"
private const val APPLICATION_PASSWORDS_URL = "$SITE_URL/wp-admin/authorize-application.php"
private const val FETCH_API_CALL_FIELDS =
"name,description,gmt_offset,url,authentication,namespaces"
}
}
Loading