Skip to content

Commit e42c789

Browse files
authored
CMM-734: request tags using wordpress-rs (#22223)
* Creatign the taxonomy client * Fetching tags from the new endpoint * detekt * Adding tests * Removing debug code * typo * Typos
1 parent ce082c1 commit e42c789

File tree

5 files changed

+250
-7
lines changed

5 files changed

+250
-7
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package org.wordpress.android.fluxc.network.rest.wpapi.taxonomy
2+
3+
import kotlinx.coroutines.CoroutineScope
4+
import kotlinx.coroutines.launch
5+
import org.wordpress.android.fluxc.Dispatcher
6+
import org.wordpress.android.fluxc.generated.TaxonomyActionBuilder
7+
import org.wordpress.android.fluxc.model.SiteModel
8+
import org.wordpress.android.fluxc.model.TermModel
9+
import org.wordpress.android.fluxc.model.TermsModel
10+
import org.wordpress.android.fluxc.module.FLUXC_SCOPE
11+
import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider
12+
import org.wordpress.android.fluxc.store.TaxonomyStore.FetchTermsResponsePayload
13+
import org.wordpress.android.fluxc.store.TaxonomyStore.TaxonomyError
14+
import org.wordpress.android.fluxc.store.TaxonomyStore.TaxonomyErrorType
15+
import org.wordpress.android.fluxc.utils.AppLogWrapper
16+
import org.wordpress.android.util.AppLog
17+
import rs.wordpress.api.kotlin.WpRequestResult
18+
import uniffi.wp_api.TagListParams
19+
import uniffi.wp_api.TagWithEditContext
20+
import javax.inject.Inject
21+
import javax.inject.Named
22+
import javax.inject.Singleton
23+
24+
@Singleton
25+
class TaxonomyRsApiRestClient @Inject constructor(
26+
@Named(FLUXC_SCOPE) private val scope: CoroutineScope,
27+
private val dispatcher: Dispatcher,
28+
private val appLogWrapper: AppLogWrapper,
29+
private val wpApiClientProvider: WpApiClientProvider,
30+
) {
31+
fun fetchPostTags(site: SiteModel, taxonomyName: String) {
32+
scope.launch {
33+
val client = wpApiClientProvider.getWpApiClient(site)
34+
35+
val mediaResponse = client.request { requestBuilder ->
36+
requestBuilder.tags().listWithEditContext(
37+
TagListParams()
38+
)
39+
}
40+
41+
val termsResponsePayload = when (mediaResponse) {
42+
is WpRequestResult.Success -> {
43+
appLogWrapper.d(AppLog.T.POSTS, "Fetched tags list: ${mediaResponse.response.data.size}")
44+
mediaResponse.response.data.toFetchTermsResponsePayload(site, taxonomyName)
45+
}
46+
47+
else -> {
48+
appLogWrapper.e(AppLog.T.POSTS, "Fetch tags list failed: $mediaResponse")
49+
FetchTermsResponsePayload(
50+
TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, ""),
51+
taxonomyName
52+
)
53+
}
54+
}
55+
notifyTagsFetched(termsResponsePayload)
56+
}
57+
}
58+
59+
private fun notifyTagsFetched(
60+
payload: FetchTermsResponsePayload,
61+
) {
62+
dispatcher.dispatch(TaxonomyActionBuilder.newFetchedTermsAction(payload))
63+
}
64+
65+
private fun List<TagWithEditContext>.toFetchTermsResponsePayload(
66+
site: SiteModel,
67+
taxonomyName: String
68+
): FetchTermsResponsePayload = FetchTermsResponsePayload(
69+
TermsModel(
70+
this.map {
71+
TermModel(
72+
it.id.toInt(),
73+
site.id,
74+
it.id,
75+
taxonomyName,
76+
it.name,
77+
it.slug,
78+
it.description,
79+
0,
80+
it.count.toInt()
81+
)
82+
}
83+
),
84+
site,
85+
taxonomyName
86+
)
87+
}

libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TaxonomyStore.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.wordpress.android.fluxc.model.TermModel;
1717
import org.wordpress.android.fluxc.model.TermsModel;
1818
import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError;
19+
import org.wordpress.android.fluxc.network.rest.wpapi.taxonomy.TaxonomyRsApiRestClient;
1920
import org.wordpress.android.fluxc.network.rest.wpcom.taxonomy.TaxonomyRestClient;
2021
import org.wordpress.android.fluxc.network.xmlrpc.taxonomy.TaxonomyXMLRPCClient;
2122
import org.wordpress.android.fluxc.persistence.TaxonomySqlUtils;
@@ -153,12 +154,15 @@ public static TaxonomyErrorType fromString(@NonNull String string) {
153154

154155
private final TaxonomyRestClient mTaxonomyRestClient;
155156
private final TaxonomyXMLRPCClient mTaxonomyXMLRPCClient;
157+
private final TaxonomyRsApiRestClient mTaxonomyRsApiRestClient;
156158

157159
@Inject public TaxonomyStore(Dispatcher dispatcher, TaxonomyRestClient taxonomyRestClient,
158-
TaxonomyXMLRPCClient taxonomyXMLRPCClient) {
160+
TaxonomyXMLRPCClient taxonomyXMLRPCClient,
161+
TaxonomyRsApiRestClient taxonomyRsApiRestClient) {
159162
super(dispatcher);
160163
mTaxonomyRestClient = taxonomyRestClient;
161164
mTaxonomyXMLRPCClient = taxonomyXMLRPCClient;
165+
mTaxonomyRsApiRestClient = taxonomyRsApiRestClient;
162166
}
163167

164168
@Override
@@ -324,12 +328,12 @@ private void fetchTerm(@NonNull RemoteTermPayload payload) {
324328
}
325329

326330
private void fetchTerms(@NonNull SiteModel site, @NonNull String taxonomyName) {
327-
// TODO: Support large number of terms (currently pulling 100 from REST, and ? from XML-RPC) - pagination?
328-
if (site.isUsingWpComRestApi()) {
331+
if (site.isUsingSelfHostedRestApi() && DEFAULT_TAXONOMY_TAG.equals(taxonomyName)) {
332+
mTaxonomyRsApiRestClient.fetchPostTags(site, taxonomyName);
333+
} else if (site.isUsingWpComRestApi()) {
329334
mTaxonomyRestClient.fetchTerms(site, taxonomyName);
330335
} else {
331-
// TODO: check for WP-REST-API plugin and use it here
332-
mTaxonomyXMLRPCClient.fetchTerms(site, taxonomyName);
336+
mTaxonomyXMLRPCClient.fetchTerms(site, taxonomyName);
333337
}
334338
}
335339

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ private const val SUFFIX_SEPARATOR = "?"
6565

6666
@ExperimentalCoroutinesApi
6767
@RunWith(RobolectricTestRunner::class)
68-
class MediaRSApiRestClientTest {
68+
class MediaRsApiRestClientTest {
6969
@Mock
7070
private lateinit var dispatcher: Dispatcher
7171
@Mock
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package org.wordpress.android.fluxc.network.rest.wpapi.taxonomy
2+
3+
import kotlinx.coroutines.CoroutineScope
4+
import kotlinx.coroutines.ExperimentalCoroutinesApi
5+
import kotlinx.coroutines.test.TestCoroutineScheduler
6+
import kotlinx.coroutines.test.UnconfinedTestDispatcher
7+
import kotlinx.coroutines.test.runTest
8+
import org.junit.Assert.assertEquals
9+
import org.junit.Assert.assertNotNull
10+
import org.junit.Assert.assertNull
11+
import org.junit.Before
12+
import org.junit.Test
13+
import org.junit.runner.RunWith
14+
import org.mockito.ArgumentCaptor
15+
import org.mockito.Mock
16+
import org.mockito.MockitoAnnotations
17+
import org.mockito.kotlin.any
18+
import org.mockito.kotlin.mock
19+
import org.mockito.kotlin.verify
20+
import org.mockito.kotlin.whenever
21+
import org.robolectric.RobolectricTestRunner
22+
import org.wordpress.android.fluxc.Dispatcher
23+
import org.wordpress.android.fluxc.action.TaxonomyAction
24+
import org.wordpress.android.fluxc.annotations.action.Action
25+
import org.wordpress.android.fluxc.model.SiteModel
26+
import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider
27+
import org.wordpress.android.fluxc.store.TaxonomyStore.FetchTermsResponsePayload
28+
import org.wordpress.android.fluxc.store.TaxonomyStore.TaxonomyErrorType
29+
import org.wordpress.android.fluxc.utils.AppLogWrapper
30+
import rs.wordpress.api.kotlin.WpApiClient
31+
import rs.wordpress.api.kotlin.WpRequestResult
32+
import uniffi.wp_api.TagWithEditContext
33+
import uniffi.wp_api.TagsRequestListWithEditContextResponse
34+
import uniffi.wp_api.TaxonomyType
35+
import uniffi.wp_api.WpNetworkHeaderMap
36+
37+
@ExperimentalCoroutinesApi
38+
@RunWith(RobolectricTestRunner::class)
39+
class TaxonomyRsApiRestClientTest {
40+
@Mock
41+
private lateinit var dispatcher: Dispatcher
42+
@Mock
43+
private lateinit var appLogWrapper: AppLogWrapper
44+
@Mock
45+
private lateinit var wpApiClientProvider: WpApiClientProvider
46+
@Mock
47+
private lateinit var wpApiClient: WpApiClient
48+
49+
private lateinit var testScope: CoroutineScope
50+
private lateinit var taxonomyClient: TaxonomyRsApiRestClient
51+
52+
private val testSite = SiteModel().apply {
53+
id = 123
54+
url = "https://test.wordpress.com"
55+
}
56+
57+
private val testTaxonomyName = "post_tag"
58+
59+
@Before
60+
fun setUp() {
61+
MockitoAnnotations.openMocks(this)
62+
63+
val testScheduler = TestCoroutineScheduler()
64+
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
65+
testScope = CoroutineScope(testDispatcher)
66+
67+
whenever(wpApiClientProvider.getWpApiClient(testSite)).thenReturn(wpApiClient)
68+
69+
taxonomyClient = TaxonomyRsApiRestClient(
70+
testScope,
71+
dispatcher,
72+
appLogWrapper,
73+
wpApiClientProvider
74+
)
75+
}
76+
77+
@Test
78+
fun `fetchPostTags with error response dispatches error action`() = runTest {
79+
// Use a concrete error type that we can create - UnknownError requires statusCode and response
80+
val errorResponse = WpRequestResult.UnknownError<Any>(
81+
statusCode = 500u,
82+
response = "Internal Server Error"
83+
)
84+
85+
whenever(wpApiClient.request<Any>(any())).thenReturn(errorResponse)
86+
87+
taxonomyClient.fetchPostTags(testSite, testTaxonomyName)
88+
89+
// Verify dispatcher was called with error action
90+
val actionCaptor = ArgumentCaptor.forClass(Action::class.java)
91+
verify(dispatcher).dispatch(actionCaptor.capture())
92+
93+
val capturedAction = actionCaptor.value
94+
val payload = capturedAction.payload as FetchTermsResponsePayload
95+
assertEquals(capturedAction.type, TaxonomyAction.FETCHED_TERMS)
96+
assertEquals(testTaxonomyName, payload.taxonomy)
97+
assertNotNull(payload.error)
98+
assertEquals(TaxonomyErrorType.GENERIC_ERROR, payload.error?.type)
99+
}
100+
101+
@Test
102+
fun `fetchPostTags with success response dispatches success action`() = runTest {
103+
val tagWithEditContext = listOf(
104+
createTestTagWithEditContext(),
105+
createTestTagWithEditContext()
106+
)
107+
108+
// Create the correct response structure following the MediaRSApiRestClientTest pattern
109+
val tagResponse = TagsRequestListWithEditContextResponse(
110+
tagWithEditContext,
111+
mock<WpNetworkHeaderMap>(),
112+
null,
113+
null
114+
)
115+
116+
val successResponse: WpRequestResult<TagsRequestListWithEditContextResponse> = WpRequestResult.Success(
117+
response = tagResponse
118+
)
119+
120+
whenever(wpApiClient.request<TagsRequestListWithEditContextResponse>(any())).thenReturn(successResponse)
121+
122+
taxonomyClient.fetchPostTags(testSite, testTaxonomyName)
123+
124+
// Verify dispatcher was called with success action
125+
val actionCaptor = ArgumentCaptor.forClass(Action::class.java)
126+
verify(dispatcher).dispatch(actionCaptor.capture())
127+
128+
val capturedAction = actionCaptor.value
129+
val payload = capturedAction.payload as FetchTermsResponsePayload
130+
assertEquals(capturedAction.type, TaxonomyAction.FETCHED_TERMS)
131+
assertEquals(testTaxonomyName, payload.taxonomy)
132+
assertEquals(testSite, payload.site)
133+
assertNotNull(payload.terms)
134+
assertEquals(2, payload.terms.terms.size)
135+
assertNull(payload.error)
136+
}
137+
138+
private fun createTestTagWithEditContext(): TagWithEditContext {
139+
return TagWithEditContext(
140+
id = 1L,
141+
count = 5L,
142+
description = "Test tag description",
143+
link = "https://example.com/tag/test",
144+
name = "Test Tag",
145+
slug = "test-tag",
146+
taxonomy = TaxonomyType.PostTag
147+
)
148+
}
149+
}
150+

libs/fluxc/src/test/java/org/wordpress/android/fluxc/taxonomy/TaxonomyStoreUnitTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.wordpress.android.fluxc.model.SiteModel;
1616
import org.wordpress.android.fluxc.model.TermModel;
1717
import org.wordpress.android.fluxc.network.rest.wpcom.taxonomy.TaxonomyRestClient;
18+
import org.wordpress.android.fluxc.network.rest.wpapi.taxonomy.TaxonomyRsApiRestClient;
1819
import org.wordpress.android.fluxc.network.xmlrpc.taxonomy.TaxonomyXMLRPCClient;
1920
import org.wordpress.android.fluxc.persistence.TaxonomySqlUtils;
2021
import org.wordpress.android.fluxc.persistence.WellSqlConfig;
@@ -32,7 +33,8 @@ public class TaxonomyStoreUnitTest {
3233
private final TaxonomyStore mTaxonomyStore = new TaxonomyStore(
3334
new Dispatcher(),
3435
Mockito.mock(TaxonomyRestClient.class),
35-
Mockito.mock(TaxonomyXMLRPCClient.class)
36+
Mockito.mock(TaxonomyXMLRPCClient.class),
37+
Mockito.mock(TaxonomyRsApiRestClient.class)
3638
);
3739

3840
@Before

0 commit comments

Comments
 (0)