From f00d50eb6b620ba954fe59292a05d306fe181c23 Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 12:29:27 +0200 Subject: [PATCH 01/19] Updating library --- .../ui/jetpackrestconnection/JetpackConnectionHelper.kt | 2 +- .../wordpress/android/ui/selfhostedusers/SampleUsers.kt | 7 ++++--- gradle/libs.versions.toml | 2 +- .../fluxc/network/rest/wpapi/rs/WpApiClientProvider.kt | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/jetpackrestconnection/JetpackConnectionHelper.kt b/WordPress/src/main/java/org/wordpress/android/ui/jetpackrestconnection/JetpackConnectionHelper.kt index dbca21b5af31..e512a5851c5b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/jetpackrestconnection/JetpackConnectionHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/jetpackrestconnection/JetpackConnectionHelper.kt @@ -58,7 +58,7 @@ class JetpackConnectionHelper @Inject constructor( site.wpApiRestUrl ?: "${site.url}/wp-json" private inner class InvalidAuthNotifier : WpAppNotifier { - override suspend fun requestedWithInvalidAuthentication() { + override suspend fun requestedWithInvalidAuthentication(requestUrl: String) { appLogWrapper.d(AppLog.T.API, "$TAG: requestedWithInvalidAuthentication") throw IllegalArgumentException("Invalid credentials") } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/selfhostedusers/SampleUsers.kt b/WordPress/src/main/java/org/wordpress/android/ui/selfhostedusers/SampleUsers.kt index 7a61d84afaef..b823d4d9b930 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/selfhostedusers/SampleUsers.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/selfhostedusers/SampleUsers.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.selfhostedusers +import uniffi.wp_api.UserRole import uniffi.wp_api.UserWithEditContext /** @@ -24,7 +25,7 @@ object SampleUsers { name = "Sample User", nickname = "User nickname", registeredDate = "2023-01-01", - roles = listOf("admin"), + roles = listOf(UserRole.Administrator), slug = "sample-user", url = "example.com", description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam non quam viverra, viverra est vel, interdum felis. Pellentesque interdum libero quis metus pharetra ullamcorper. Morbi nec libero ligula. Quisque consectetur, purus sit amet lobortis porttitor, ligula ex imperdiet massa, in ullamcorper augue odio sit amet metus. In sollicitudin mauris et risus mollis commodo. Aliquam vel vehicula ante, nec blandit erat. Aenean non turpis porttitor orci fringilla fringilla nec ac nunc. Nulla ultrices urna ut ipsum posuere blandit. Phasellus mauris nulla, tincidunt at leo at, auctor interdum felis. Sed pharetra risus a ullamcorper dictum. Suspendisse pharetra justo molestie risus lobortis facilisis.", @@ -45,7 +46,7 @@ object SampleUsers { name = "Sample User", nickname = "User nickname", registeredDate = "2023-01-01", - roles = listOf("contributor"), + roles = listOf(UserRole.Contributor), slug = "sample-user", url = "example.com", ) @@ -65,7 +66,7 @@ object SampleUsers { name = "Sample User", nickname = "User nickname", registeredDate = "2023-01-01", - roles = listOf("contributor", "editor", "subscriber"), + roles = listOf(UserRole.Contributor, UserRole.Editor, UserRole.Subscriber), slug = "sample-user", url = "example.com", ) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4dae94486c6c..3140bada2895 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -102,7 +102,7 @@ wiremock = '2.26.3' wordpress-aztec = 'v2.1.4' wordpress-lint = '2.2.0' wordpress-persistent-edittext = '1.0.2' -wordpress-rs = 'trunk-f4e2450ca5545a4909cb08273f37a7f694244921' +wordpress-rs = 'trunk-d0c9eebab77e8701810077ac1fba7d39ef8d121f' wordpress-utils = '3.14.0' automattic-ucrop = '2.2.11' zendesk = '5.5.0' diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/rs/WpApiClientProvider.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/rs/WpApiClientProvider.kt index 4cd6033ec259..8fe5542ff4be 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/rs/WpApiClientProvider.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/rs/WpApiClientProvider.kt @@ -25,7 +25,7 @@ class WpApiClientProvider @Inject constructor( authProvider = authProvider, requestExecutor = WpRequestExecutor(uploadListener = uploadListener), appNotifier = object : WpAppNotifier { - override suspend fun requestedWithInvalidAuthentication() { + override suspend fun requestedWithInvalidAuthentication(requestUrl: String) { wpAppNotifierHandler.notifyRequestedWithInvalidAuthentication(site) } } From dc07310ede094f8d5e197821514fb8687fc3063d Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 12:31:28 +0200 Subject: [PATCH 02/19] Porting the terms fetching --- .../wpapi/taxonomy/TaxonomyRsApiRestClient.kt | 510 ++++++++---------- 1 file changed, 225 insertions(+), 285 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt index 5721a5420800..ee8e55286dcc 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt @@ -19,16 +19,8 @@ import org.wordpress.android.fluxc.utils.AppLogWrapper import org.wordpress.android.util.AppLog import rs.wordpress.api.kotlin.WpApiClient import rs.wordpress.api.kotlin.WpRequestResult -import uniffi.wp_api.CategoryCreateParams -import uniffi.wp_api.CategoryDeleteResponse -import uniffi.wp_api.CategoryListParams -import uniffi.wp_api.CategoryUpdateParams -import uniffi.wp_api.CategoryWithEditContext -import uniffi.wp_api.TagCreateParams -import uniffi.wp_api.TagDeleteResponse -import uniffi.wp_api.TagListParams -import uniffi.wp_api.TagUpdateParams -import uniffi.wp_api.TagWithEditContext +import uniffi.wp_api.TermEndpointType +import uniffi.wp_api.TermListParams import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton @@ -57,18 +49,21 @@ class TaxonomyRsApiRestClient @Inject constructor( term: TermModel, site: SiteModel ) { - val categoriesResponse = client.request { requestBuilder -> - requestBuilder.categories().delete(categoryId = term.id.toLong()) - } - handleDeleteResponse( - response = categoriesResponse, - termType = "category", - term = term, - site = site, - taxonomy = DEFAULT_TAXONOMY_CATEGORY, - extractData = { it.response.data }, - checkDeleted = { data -> (data as CategoryDeleteResponse).deleted } - ) +// val categoriesResponse = client.request { requestBuilder -> +// requestBuilder.terms().delete( +// termEndpointType = TermEndpointType.Tags, +// termId = term.id.toLong() +// ) +// } +// handleDeleteResponse( +// response = categoriesResponse, +// termType = "category", +// term = term, +// site = site, +// taxonomy = DEFAULT_TAXONOMY_CATEGORY, +// extractData = { it.response.data }, +// checkDeleted = { data -> (data as CategoryDeleteResponse).deleted } +// ) } private suspend fun deleteTag( @@ -76,18 +71,18 @@ class TaxonomyRsApiRestClient @Inject constructor( term: TermModel, site: SiteModel ) { - val tagsResponse = client.request { requestBuilder -> - requestBuilder.tags().delete(tagId = term.id.toLong()) - } - handleDeleteResponse( - response = tagsResponse, - termType = "tag", - term = term, - site = site, - taxonomy = DEFAULT_TAXONOMY_TAG, - extractData = { it.response.data }, - checkDeleted = { data -> (data as TagDeleteResponse).deleted } - ) +// val tagsResponse = client.request { requestBuilder -> +// requestBuilder.tags().delete(tagId = term.id.toLong()) +// } +// handleDeleteResponse( +// response = tagsResponse, +// termType = "tag", +// term = term, +// site = site, +// taxonomy = DEFAULT_TAXONOMY_TAG, +// extractData = { it.response.data }, +// checkDeleted = { data -> (data as TagDeleteResponse).deleted } +// ) } @Suppress("LongParameterList") @@ -141,38 +136,38 @@ class TaxonomyRsApiRestClient @Inject constructor( term: TermModel, site: SiteModel ) { - val categoriesResponse = client.request { requestBuilder -> - requestBuilder.categories().create( - CategoryCreateParams( - name = term.name, - description = term.description, - slug = term.slug, - parent = term.parentRemoteId - ) - ) - } - - handleCreateResponse( - response = categoriesResponse, - termType = "category", - term = term, - site = site, - extractData = { it.response.data }, - createTermModel = { data -> - val category = data as CategoryWithEditContext - TermModel( - category.id.toInt(), - site.id, - category.id, - DEFAULT_TAXONOMY_CATEGORY, - category.name, - category.slug, - category.description, - category.parent, - category.count.toInt() - ) - } - ) +// val categoriesResponse = client.request { requestBuilder -> +// requestBuilder.categories().create( +// CategoryCreateParams( +// name = term.name, +// description = term.description, +// slug = term.slug, +// parent = term.parentRemoteId +// ) +// ) +// } +// +// handleCreateResponse( +// response = categoriesResponse, +// termType = "category", +// term = term, +// site = site, +// extractData = { it.response.data }, +// createTermModel = { data -> +// val category = data as CategoryWithEditContext +// TermModel( +// category.id.toInt(), +// site.id, +// category.id, +// DEFAULT_TAXONOMY_CATEGORY, +// category.name, +// category.slug, +// category.description, +// category.parent, +// category.count.toInt() +// ) +// } +// ) } private suspend fun createTag( @@ -180,36 +175,36 @@ class TaxonomyRsApiRestClient @Inject constructor( term: TermModel, site: SiteModel ) { - val tagResponse = client.request { requestBuilder -> - requestBuilder.tags().create( - TagCreateParams( - name = term.name, - description = term.description, - slug = term.slug, - ) - ) - } - handleCreateResponse( - response = tagResponse, - termType = "tag", - term = term, - site = site, - extractData = { it.response.data }, - createTermModel = { data -> - val tag = data as TagWithEditContext - TermModel( - tag.id.toInt(), - site.id, - tag.id, - DEFAULT_TAXONOMY_TAG, - tag.name, - tag.slug, - tag.description, - 0, - tag.count.toInt() - ) - } - ) +// val tagResponse = client.request { requestBuilder -> +// requestBuilder.tags().create( +// TagCreateParams( +// name = term.name, +// description = term.description, +// slug = term.slug, +// ) +// ) +// } +// handleCreateResponse( +// response = tagResponse, +// termType = "tag", +// term = term, +// site = site, +// extractData = { it.response.data }, +// createTermModel = { data -> +// val tag = data as TagWithEditContext +// TermModel( +// tag.id.toInt(), +// site.id, +// tag.id, +// DEFAULT_TAXONOMY_TAG, +// tag.name, +// tag.slug, +// tag.description, +// 0, +// tag.count.toInt() +// ) +// } +// ) } @Suppress("LongParameterList") @@ -221,29 +216,29 @@ class TaxonomyRsApiRestClient @Inject constructor( extractData: (WpRequestResult.Success) -> Any, createTermModel: (Any) -> TermModel ) { - when (response) { - is WpRequestResult.Success -> { - val data = extractData(response) - val name = when (data) { - is CategoryWithEditContext -> data.name - is TagWithEditContext -> data.name - else -> "unknown" - } - appLogWrapper.d(AppLog.T.POSTS, "Created $termType: $name") - val payload = RemoteTermPayload(createTermModel(data), site) - notifyTermCreated(payload) - } - else -> { - notifyFailedOperation( - operation = "creating", - termType = termType, - term = term, - site = site, - errorDetails = response.toString(), - notifier = ::notifyTermCreated - ) - } - } +// when (response) { +// is WpRequestResult.Success -> { +// val data = extractData(response) +// val name = when (data) { +// is CategoryWithEditContext -> data.name +// is TagWithEditContext -> data.name +// else -> "unknown" +// } +// appLogWrapper.d(AppLog.T.POSTS, "Created $termType: $name") +// val payload = RemoteTermPayload(createTermModel(data), site) +// notifyTermCreated(payload) +// } +// else -> { +// notifyFailedOperation( +// operation = "creating", +// termType = termType, +// term = term, +// site = site, +// errorDetails = response.toString(), +// notifier = ::notifyTermCreated +// ) +// } +// } } fun updateTerm(site: SiteModel, term: TermModel) { @@ -271,38 +266,38 @@ class TaxonomyRsApiRestClient @Inject constructor( term: TermModel, site: SiteModel ) { - val categoriesResponse = client.request { requestBuilder -> - requestBuilder.categories().update( - categoryId = term.remoteTermId, - params = CategoryUpdateParams( - name = term.name, - description = term.description, - slug = term.slug, - parent = term.parentRemoteId - ) - ) - } - handleUpdateResponse( - response = categoriesResponse, - termType = "category", - term = term, - site = site, - extractData = { it.response.data }, - createTermModel = { data -> - val category = data as CategoryWithEditContext - TermModel( - category.id.toInt(), - site.id, - category.id, - DEFAULT_TAXONOMY_CATEGORY, - category.name, - category.slug, - category.description, - category.parent, - category.count.toInt() - ) - } - ) +// val categoriesResponse = client.request { requestBuilder -> +// requestBuilder.categories().update( +// categoryId = term.remoteTermId, +// params = CategoryUpdateParams( +// name = term.name, +// description = term.description, +// slug = term.slug, +// parent = term.parentRemoteId +// ) +// ) +// } +// handleUpdateResponse( +// response = categoriesResponse, +// termType = "category", +// term = term, +// site = site, +// extractData = { it.response.data }, +// createTermModel = { data -> +// val category = data as CategoryWithEditContext +// TermModel( +// category.id.toInt(), +// site.id, +// category.id, +// DEFAULT_TAXONOMY_CATEGORY, +// category.name, +// category.slug, +// category.description, +// category.parent, +// category.count.toInt() +// ) +// } +// ) } private suspend fun updateTag( @@ -310,37 +305,37 @@ class TaxonomyRsApiRestClient @Inject constructor( term: TermModel, site: SiteModel ) { - val tagResponse = client.request { requestBuilder -> - requestBuilder.tags().update( - tagId = term.remoteTermId, - params = TagUpdateParams( - name = term.name, - description = term.description, - slug = term.slug, - ) - ) - } - handleUpdateResponse( - response = tagResponse, - termType = "tag", - term = term, - site = site, - extractData = { it.response.data }, - createTermModel = { data -> - val tag = data as TagWithEditContext - TermModel( - tag.id.toInt(), - site.id, - tag.id, - DEFAULT_TAXONOMY_TAG, - tag.name, - tag.slug, - tag.description, - 0, - tag.count.toInt() - ) - } - ) +// val tagResponse = client.request { requestBuilder -> +// requestBuilder.tags().update( +// tagId = term.remoteTermId, +// params = TagUpdateParams( +// name = term.name, +// description = term.description, +// slug = term.slug, +// ) +// ) +// } +// handleUpdateResponse( +// response = tagResponse, +// termType = "tag", +// term = term, +// site = site, +// extractData = { it.response.data }, +// createTermModel = { data -> +// val tag = data as TagWithEditContext +// TermModel( +// tag.id.toInt(), +// site.id, +// tag.id, +// DEFAULT_TAXONOMY_TAG, +// tag.name, +// tag.slug, +// tag.description, +// 0, +// tag.count.toInt() +// ) +// } +// ) } @Suppress("LongParameterList") @@ -352,29 +347,29 @@ class TaxonomyRsApiRestClient @Inject constructor( extractData: (WpRequestResult.Success) -> Any, createTermModel: (Any) -> TermModel ) { - when (response) { - is WpRequestResult.Success -> { - val data = extractData(response) - val name = when (data) { - is CategoryWithEditContext -> data.name - is TagWithEditContext -> data.name - else -> "unknown" - } - appLogWrapper.d(AppLog.T.POSTS, "${termType.replaceFirstChar { it.uppercase() }} updated: $name") - val payload = RemoteTermPayload(createTermModel(data), site) - notifyTermCreated(payload) // FluxC uses notifyTermCreated for updates - } - else -> { - notifyFailedOperation( - operation = "updating", - termType = termType, - term = term, - site = site, - errorDetails = response.toString(), - notifier = ::notifyTermCreated - ) - } - } +// when (response) { +// is WpRequestResult.Success -> { +// val data = extractData(response) +// val name = when (data) { +// is CategoryWithEditContext -> data.name +// is TagWithEditContext -> data.name +// else -> "unknown" +// } +// appLogWrapper.d(AppLog.T.POSTS, "${termType.replaceFirstChar { it.uppercase() }} updated: $name") +// val payload = RemoteTermPayload(createTermModel(data), site) +// notifyTermCreated(payload) // FluxC uses notifyTermCreated for updates +// } +// else -> { +// notifyFailedOperation( +// operation = "updating", +// termType = termType, +// term = term, +// site = site, +// errorDetails = response.toString(), +// notifier = ::notifyTermCreated +// ) +// } +// } } fun fetchTerms(site: SiteModel, taxonomyName: String) { @@ -382,103 +377,48 @@ class TaxonomyRsApiRestClient @Inject constructor( val client = wpApiClientProvider.getWpApiClient(site) when (taxonomyName) { - DEFAULT_TAXONOMY_CATEGORY -> fetchCategories(client, site) - DEFAULT_TAXONOMY_TAG -> fetchTags(client, site) + DEFAULT_TAXONOMY_CATEGORY -> fetchTerms(client, TermEndpointType.Categories, DEFAULT_TAXONOMY_CATEGORY, site) + DEFAULT_TAXONOMY_TAG -> fetchTerms(client, TermEndpointType.Tags, DEFAULT_TAXONOMY_TAG, site) else -> {} // TODO We are not supporting any other taxonomy yet } } } - private suspend fun fetchCategories( - client: WpApiClient, - site: SiteModel - ) { - val categoriesResponse = client.request { requestBuilder -> - requestBuilder.categories().listWithEditContext( - CategoryListParams() - ) - } - handleFetchResponse( - response = categoriesResponse, - termType = "categories", - taxonomy = DEFAULT_TAXONOMY_CATEGORY, - site = site, - extractData = { it.response.data }, - createTermModels = { data -> - (data as List<*>).map { category -> - val cat = category as CategoryWithEditContext - TermModel( - cat.id.toInt(), - site.id, - cat.id, - DEFAULT_TAXONOMY_CATEGORY, - cat.name, - cat.slug, - cat.description, - cat.parent, - cat.count.toInt() - ) - } - } - ) - } - - private suspend fun fetchTags( + private suspend fun fetchTerms( client: WpApiClient, + termEndpointType: TermEndpointType, + taxonomy: String, site: SiteModel ) { - val tagsResponse = client.request { requestBuilder -> - requestBuilder.tags().listWithEditContext( - TagListParams() + val termsResponse = client.request { requestBuilder -> + requestBuilder.terms().listWithEditContext( + termEndpointType = termEndpointType, + params = TermListParams() ) } - handleFetchResponse( - response = tagsResponse, - termType = "tags", - taxonomy = DEFAULT_TAXONOMY_TAG, - site = site, - extractData = { it.response.data }, - createTermModels = { data -> - (data as List<*>).map { tag -> - val t = tag as TagWithEditContext - TermModel( - t.id.toInt(), - site.id, - t.id, - DEFAULT_TAXONOMY_TAG, - t.name, - t.slug, - t.description, - 0, - t.count.toInt() - ) - } - } - ) - } - - @Suppress("LongParameterList") - private inline fun handleFetchResponse( - response: WpRequestResult, - termType: String, - taxonomy: String, - site: SiteModel, - extractData: (WpRequestResult.Success) -> Any, - createTermModels: (Any) -> List - ) { - val termsResponsePayload = when (response) { + val termsResponsePayload = when (termsResponse) { is WpRequestResult.Success -> { - val data = extractData(response) - val dataList = data as List<*> - appLogWrapper.d(AppLog.T.POSTS, "Fetched $termType list: ${dataList.size}") + appLogWrapper.d(AppLog.T.POSTS, "Fetched $termEndpointType list: ${termsResponse.response.data.size}") createTermsResponsePayload( - createTermModels(data), + terms = termsResponse.response.data.map { term -> + TermModel( + term.id.toInt(), + site.id, + term.id, + taxonomy, + term.name, + term.slug, + term.description, + term.parent ?: 0, + term.count.toInt() + ) + }, site, taxonomy ) } else -> { - appLogWrapper.e(AppLog.T.POSTS, "Fetch $termType list failed: $response") + appLogWrapper.e(AppLog.T.POSTS, "Fetch $termEndpointType list failed: $termsResponse") createErrorResponsePayload(taxonomy) } } From 93582433f4e3c35ea390b48359feeb03b1b49674 Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 12:50:19 +0200 Subject: [PATCH 03/19] Porting create --- .../wpapi/taxonomy/TaxonomyRsApiRestClient.kt | 213 ++++++------------ 1 file changed, 74 insertions(+), 139 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt index ee8e55286dcc..9277bb2951eb 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt @@ -19,6 +19,7 @@ import org.wordpress.android.fluxc.utils.AppLogWrapper import org.wordpress.android.util.AppLog import rs.wordpress.api.kotlin.WpApiClient import rs.wordpress.api.kotlin.WpRequestResult +import uniffi.wp_api.TermCreateParams import uniffi.wp_api.TermEndpointType import uniffi.wp_api.TermListParams import javax.inject.Inject @@ -121,146 +122,82 @@ class TaxonomyRsApiRestClient @Inject constructor( fun createTerm(site: SiteModel, term: TermModel) { scope.launch { - val client = wpApiClientProvider.getWpApiClient(site) - when (term.taxonomy) { - DEFAULT_TAXONOMY_CATEGORY -> createCategory(client, term, site) - DEFAULT_TAXONOMY_TAG -> createTag(client, term, site) + DEFAULT_TAXONOMY_CATEGORY -> createTerm(TermEndpointType.Categories, term, site) + DEFAULT_TAXONOMY_TAG -> createTerm(TermEndpointType.Tags, term, site) else -> {} // TODO We are not supporting any other taxonomy yet } } } - private suspend fun createCategory( - client: WpApiClient, + private suspend fun createTerm( + termEndpointType: TermEndpointType, term: TermModel, site: SiteModel ) { -// val categoriesResponse = client.request { requestBuilder -> -// requestBuilder.categories().create( -// CategoryCreateParams( -// name = term.name, -// description = term.description, -// slug = term.slug, -// parent = term.parentRemoteId -// ) -// ) -// } -// -// handleCreateResponse( -// response = categoriesResponse, -// termType = "category", -// term = term, -// site = site, -// extractData = { it.response.data }, -// createTermModel = { data -> -// val category = data as CategoryWithEditContext -// TermModel( -// category.id.toInt(), -// site.id, -// category.id, -// DEFAULT_TAXONOMY_CATEGORY, -// category.name, -// category.slug, -// category.description, -// category.parent, -// category.count.toInt() -// ) -// } -// ) - } + val client = wpApiClientProvider.getWpApiClient(site) + val taxonomyName = termEndpointType.toTaxonomyName() + val termResponse = client.request { requestBuilder -> + requestBuilder.terms().create( + termEndpointType = termEndpointType, + TermCreateParams( + name = term.name, + description = term.description, + slug = term.slug, + parent = if (term.parentRemoteId > 0) term.parentRemoteId else null + ) + ) + } - private suspend fun createTag( - client: WpApiClient, - term: TermModel, - site: SiteModel - ) { -// val tagResponse = client.request { requestBuilder -> -// requestBuilder.tags().create( -// TagCreateParams( -// name = term.name, -// description = term.description, -// slug = term.slug, -// ) -// ) -// } -// handleCreateResponse( -// response = tagResponse, -// termType = "tag", -// term = term, -// site = site, -// extractData = { it.response.data }, -// createTermModel = { data -> -// val tag = data as TagWithEditContext -// TermModel( -// tag.id.toInt(), -// site.id, -// tag.id, -// DEFAULT_TAXONOMY_TAG, -// tag.name, -// tag.slug, -// tag.description, -// 0, -// tag.count.toInt() -// ) -// } -// ) + when (termResponse) { + is WpRequestResult.Success -> { + val term = termResponse.response.data + appLogWrapper.d(AppLog.T.POSTS, "Created $taxonomyName: ${term.name}") + val payload = RemoteTermPayload( + TermModel( + term.id.toInt(), + site.id, + term.id, + taxonomyName, + term.name, + term.slug, + term.description, + term.parent ?: 0, + term.count.toInt() + ), + site + ) + dispatcher.dispatch(TaxonomyActionBuilder.newPushedTermAction(payload)) + } + else -> { + appLogWrapper.e(AppLog.T.POSTS, "Failed creating $taxonomyName: ${term.name} - $termResponse") + val payload = RemoteTermPayload(term, site) + payload.error = TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, "") + dispatcher.dispatch(TaxonomyActionBuilder.newPushedTermAction(payload)) + } + } } - @Suppress("LongParameterList") - private inline fun handleCreateResponse( - response: WpRequestResult, - termType: String, - term: TermModel, - site: SiteModel, - extractData: (WpRequestResult.Success) -> Any, - createTermModel: (Any) -> TermModel - ) { -// when (response) { -// is WpRequestResult.Success -> { -// val data = extractData(response) -// val name = when (data) { -// is CategoryWithEditContext -> data.name -// is TagWithEditContext -> data.name -// else -> "unknown" -// } -// appLogWrapper.d(AppLog.T.POSTS, "Created $termType: $name") -// val payload = RemoteTermPayload(createTermModel(data), site) -// notifyTermCreated(payload) + fun updateTerm(site: SiteModel, term: TermModel) { +// scope.launch { +// if (term.remoteTermId < 0) { +// appLogWrapper.e(AppLog.T.POSTS, "Failed updating term: $term - id <= 0") +// val payload = RemoteTermPayload(term, site) +// payload.error = TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, "") +// notifyTermCreated(payload) // FluxC uses notifyTermCreated for updates +// return@launch // } -// else -> { -// notifyFailedOperation( -// operation = "creating", -// termType = termType, -// term = term, -// site = site, -// errorDetails = response.toString(), -// notifier = ::notifyTermCreated -// ) +// +// val client = wpApiClientProvider.getWpApiClient(site) +// +// when (term.taxonomy) { +// DEFAULT_TAXONOMY_CATEGORY -> updateCategory(client, term, site) +// DEFAULT_TAXONOMY_TAG -> updateTag(client, term, site) +// else -> {} // TODO We are not supporting any other taxonomy yet // } // } } - fun updateTerm(site: SiteModel, term: TermModel) { - scope.launch { - if (term.remoteTermId < 0) { - appLogWrapper.e(AppLog.T.POSTS, "Failed updating term: $term - id <= 0") - val payload = RemoteTermPayload(term, site) - payload.error = TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, "") - notifyTermCreated(payload) // FluxC uses notifyTermCreated for updates - return@launch - } - - val client = wpApiClientProvider.getWpApiClient(site) - - when (term.taxonomy) { - DEFAULT_TAXONOMY_CATEGORY -> updateCategory(client, term, site) - DEFAULT_TAXONOMY_TAG -> updateTag(client, term, site) - else -> {} // TODO We are not supporting any other taxonomy yet - } - } - } - private suspend fun updateCategory( client: WpApiClient, term: TermModel, @@ -374,22 +311,20 @@ class TaxonomyRsApiRestClient @Inject constructor( fun fetchTerms(site: SiteModel, taxonomyName: String) { scope.launch { - val client = wpApiClientProvider.getWpApiClient(site) - when (taxonomyName) { - DEFAULT_TAXONOMY_CATEGORY -> fetchTerms(client, TermEndpointType.Categories, DEFAULT_TAXONOMY_CATEGORY, site) - DEFAULT_TAXONOMY_TAG -> fetchTerms(client, TermEndpointType.Tags, DEFAULT_TAXONOMY_TAG, site) + DEFAULT_TAXONOMY_CATEGORY -> fetchTerms(TermEndpointType.Categories, site) + DEFAULT_TAXONOMY_TAG -> fetchTerms(TermEndpointType.Tags, site) else -> {} // TODO We are not supporting any other taxonomy yet } } } private suspend fun fetchTerms( - client: WpApiClient, termEndpointType: TermEndpointType, - taxonomy: String, site: SiteModel ) { + val client = wpApiClientProvider.getWpApiClient(site) + val taxonomyName = termEndpointType.toTaxonomyName() val termsResponse = client.request { requestBuilder -> requestBuilder.terms().listWithEditContext( termEndpointType = termEndpointType, @@ -398,14 +333,14 @@ class TaxonomyRsApiRestClient @Inject constructor( } val termsResponsePayload = when (termsResponse) { is WpRequestResult.Success -> { - appLogWrapper.d(AppLog.T.POSTS, "Fetched $termEndpointType list: ${termsResponse.response.data.size}") + appLogWrapper.d(AppLog.T.POSTS, "Fetched $taxonomyName list: ${termsResponse.response.data.size}") createTermsResponsePayload( terms = termsResponse.response.data.map { term -> TermModel( term.id.toInt(), site.id, term.id, - taxonomy, + taxonomyName, term.name, term.slug, term.description, @@ -414,12 +349,12 @@ class TaxonomyRsApiRestClient @Inject constructor( ) }, site, - taxonomy + taxonomyName ) } else -> { appLogWrapper.e(AppLog.T.POSTS, "Fetch $termEndpointType list failed: $termsResponse") - createErrorResponsePayload(taxonomy) + createErrorResponsePayload(taxonomyName) } } notifyTermsFetched(termsResponsePayload) @@ -431,12 +366,6 @@ class TaxonomyRsApiRestClient @Inject constructor( dispatcher.dispatch(TaxonomyActionBuilder.newFetchedTermsAction(payload)) } - private fun notifyTermCreated( - payload: RemoteTermPayload, - ) { - dispatcher.dispatch(TaxonomyActionBuilder.newPushedTermAction(payload)) - } - private fun notifyTermDeleted( payload: RemoteTermPayload, ) { @@ -488,4 +417,10 @@ class TaxonomyRsApiRestClient @Inject constructor( site, taxonomyName ) + + private fun TermEndpointType.toTaxonomyName(): String = when (this) { + TermEndpointType.Categories -> DEFAULT_TAXONOMY_CATEGORY + TermEndpointType.Tags -> DEFAULT_TAXONOMY_TAG + is TermEndpointType.Custom -> this.v1 + } } From 9bb9e357e295726b4366f9e297cd8f6bb4d8fc70 Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 13:14:42 +0200 Subject: [PATCH 04/19] Porting delete term --- .../wpapi/taxonomy/TaxonomyRsApiRestClient.kt | 105 +++++------------- 1 file changed, 30 insertions(+), 75 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt index 9277bb2951eb..b7e6964611ea 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt @@ -35,86 +35,55 @@ class TaxonomyRsApiRestClient @Inject constructor( ) { fun deleteTerm(site: SiteModel, term: TermModel) { scope.launch { - val client = wpApiClientProvider.getWpApiClient(site) - when (term.taxonomy) { - DEFAULT_TAXONOMY_CATEGORY -> deleteCategory(client, term, site) - DEFAULT_TAXONOMY_TAG -> deleteTag(client, term, site) + DEFAULT_TAXONOMY_CATEGORY -> deleteTerm(TermEndpointType.Categories, term, site) + DEFAULT_TAXONOMY_TAG -> deleteTerm(TermEndpointType.Tags, term, site) else -> {} // TODO We are not supporting any other taxonomy yet } } } - private suspend fun deleteCategory( - client: WpApiClient, - term: TermModel, - site: SiteModel - ) { -// val categoriesResponse = client.request { requestBuilder -> -// requestBuilder.terms().delete( -// termEndpointType = TermEndpointType.Tags, -// termId = term.id.toLong() -// ) -// } -// handleDeleteResponse( -// response = categoriesResponse, -// termType = "category", -// term = term, -// site = site, -// taxonomy = DEFAULT_TAXONOMY_CATEGORY, -// extractData = { it.response.data }, -// checkDeleted = { data -> (data as CategoryDeleteResponse).deleted } -// ) - } - - private suspend fun deleteTag( - client: WpApiClient, + private suspend fun deleteTerm( + termEndpointType: TermEndpointType, term: TermModel, site: SiteModel ) { -// val tagsResponse = client.request { requestBuilder -> -// requestBuilder.tags().delete(tagId = term.id.toLong()) -// } -// handleDeleteResponse( -// response = tagsResponse, -// termType = "tag", -// term = term, -// site = site, -// taxonomy = DEFAULT_TAXONOMY_TAG, -// extractData = { it.response.data }, -// checkDeleted = { data -> (data as TagDeleteResponse).deleted } -// ) - } - - @Suppress("LongParameterList") - private inline fun handleDeleteResponse( - response: WpRequestResult, - termType: String, - term: TermModel, - site: SiteModel, - taxonomy: String, - extractData: (WpRequestResult.Success) -> Any, - checkDeleted: (Any) -> Boolean - ) { - when (response) { + val client = wpApiClientProvider.getWpApiClient(site) + val taxonomyName = termEndpointType.toTaxonomyName() + val termResponse = client.request { requestBuilder -> + requestBuilder.terms().delete( + termEndpointType = TermEndpointType.Tags, + termId = term.id.toLong() + ) + } + when (termResponse) { is WpRequestResult.Success -> { - val data = extractData(response) - appLogWrapper.d(AppLog.T.POSTS, "Deleted $termType: ${term.name} - ${checkDeleted(data)}") - if (checkDeleted(data)) { - val termModel = createTermModelForDelete(term, site, taxonomy) + appLogWrapper.d(AppLog.T.POSTS, "Deleting $taxonomyName: ${term.name} - ${termResponse.response.data.deleted}") + if (termResponse.response.data.deleted) { + val termModel = TermModel( + term.id, + site.id, + term.id.toLong(), + taxonomyName, + term.name, + term.slug, + term.description, + term.parentRemoteId, + term.postCount + ) notifyTermDeleted(RemoteTermPayload(termModel, site)) } else { - notifyFailedDeleting(termType, site, term) + notifyFailedDeleting(taxonomyName, site, term) } } else -> { - notifyFailedDeleting(termType, site, term) + notifyFailedDeleting(taxonomyName, site, term) } } } - private fun notifyFailedDeleting(termType: String, site: SiteModel, term: TermModel) { - appLogWrapper.e(AppLog.T.POSTS, "Failed deleting $termType") + private fun notifyFailedDeleting(taxonomyName: String, site: SiteModel, term: TermModel) { + appLogWrapper.e(AppLog.T.POSTS, "Failed deleting $taxonomyName") val payload = RemoteTermPayload(term, site) payload.error = TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, "") notifyTermDeleted(payload) @@ -394,20 +363,6 @@ class TaxonomyRsApiRestClient @Inject constructor( ) - private fun createTermModelForDelete(term: TermModel, site: SiteModel, taxonomy: String): TermModel { - return TermModel( - term.id, - site.id, - term.id.toLong(), - taxonomy, - term.name, - term.slug, - term.description, - term.parentRemoteId, - term.postCount - ) - } - private fun createTermsResponsePayload( terms: List, site: SiteModel, From 5e62ed7f61a4cccb2ddd389aab42d28623eba925 Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 13:31:33 +0200 Subject: [PATCH 05/19] porting update term --- .../wpapi/taxonomy/TaxonomyRsApiRestClient.kt | 268 ++++++------------ 1 file changed, 89 insertions(+), 179 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt index b7e6964611ea..7204a6f14aad 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt @@ -17,11 +17,11 @@ import org.wordpress.android.fluxc.store.TaxonomyStore.TaxonomyError import org.wordpress.android.fluxc.store.TaxonomyStore.TaxonomyErrorType import org.wordpress.android.fluxc.utils.AppLogWrapper import org.wordpress.android.util.AppLog -import rs.wordpress.api.kotlin.WpApiClient import rs.wordpress.api.kotlin.WpRequestResult import uniffi.wp_api.TermCreateParams import uniffi.wp_api.TermEndpointType import uniffi.wp_api.TermListParams +import uniffi.wp_api.TermUpdateParams import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton @@ -89,6 +89,12 @@ class TaxonomyRsApiRestClient @Inject constructor( notifyTermDeleted(payload) } + private fun notifyTermDeleted( + payload: RemoteTermPayload, + ) { + dispatcher.dispatch(TaxonomyActionBuilder.newDeletedTermAction(payload)) + } + fun createTerm(site: SiteModel, term: TermModel) { scope.launch { when (term.taxonomy) { @@ -148,134 +154,76 @@ class TaxonomyRsApiRestClient @Inject constructor( } fun updateTerm(site: SiteModel, term: TermModel) { -// scope.launch { -// if (term.remoteTermId < 0) { -// appLogWrapper.e(AppLog.T.POSTS, "Failed updating term: $term - id <= 0") -// val payload = RemoteTermPayload(term, site) -// payload.error = TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, "") -// notifyTermCreated(payload) // FluxC uses notifyTermCreated for updates -// return@launch -// } -// -// val client = wpApiClientProvider.getWpApiClient(site) -// -// when (term.taxonomy) { -// DEFAULT_TAXONOMY_CATEGORY -> updateCategory(client, term, site) -// DEFAULT_TAXONOMY_TAG -> updateTag(client, term, site) -// else -> {} // TODO We are not supporting any other taxonomy yet -// } -// } - } + scope.launch { + if (term.remoteTermId < 0) { + appLogWrapper.e(AppLog.T.POSTS, "Failed updating term: $term - id <= 0") + val payload = RemoteTermPayload(term, site) + payload.error = TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, "") + notifyTermUpdated(payload) + return@launch + } - private suspend fun updateCategory( - client: WpApiClient, - term: TermModel, - site: SiteModel - ) { -// val categoriesResponse = client.request { requestBuilder -> -// requestBuilder.categories().update( -// categoryId = term.remoteTermId, -// params = CategoryUpdateParams( -// name = term.name, -// description = term.description, -// slug = term.slug, -// parent = term.parentRemoteId -// ) -// ) -// } -// handleUpdateResponse( -// response = categoriesResponse, -// termType = "category", -// term = term, -// site = site, -// extractData = { it.response.data }, -// createTermModel = { data -> -// val category = data as CategoryWithEditContext -// TermModel( -// category.id.toInt(), -// site.id, -// category.id, -// DEFAULT_TAXONOMY_CATEGORY, -// category.name, -// category.slug, -// category.description, -// category.parent, -// category.count.toInt() -// ) -// } -// ) + when (term.taxonomy) { + DEFAULT_TAXONOMY_CATEGORY -> updateTerm(TermEndpointType.Categories, term, site) + DEFAULT_TAXONOMY_TAG -> updateTerm(TermEndpointType.Tags, term, site) + else -> {} // TODO We are not supporting any other taxonomy yet + } + } } - private suspend fun updateTag( - client: WpApiClient, + private suspend fun updateTerm( + termEndpointType: TermEndpointType, term: TermModel, site: SiteModel ) { -// val tagResponse = client.request { requestBuilder -> -// requestBuilder.tags().update( -// tagId = term.remoteTermId, -// params = TagUpdateParams( -// name = term.name, -// description = term.description, -// slug = term.slug, -// ) -// ) -// } -// handleUpdateResponse( -// response = tagResponse, -// termType = "tag", -// term = term, -// site = site, -// extractData = { it.response.data }, -// createTermModel = { data -> -// val tag = data as TagWithEditContext -// TermModel( -// tag.id.toInt(), -// site.id, -// tag.id, -// DEFAULT_TAXONOMY_TAG, -// tag.name, -// tag.slug, -// tag.description, -// 0, -// tag.count.toInt() -// ) -// } -// ) + val client = wpApiClientProvider.getWpApiClient(site) + val taxonomyName = termEndpointType.toTaxonomyName() + val termResponse = client.request { requestBuilder -> + requestBuilder.terms().update( + termEndpointType = termEndpointType, + termId = term.remoteTermId, + params = TermUpdateParams( + name = term.name, + description = term.description, + slug = term.slug, + parent = if (term.parentRemoteId > 0) term.parentRemoteId else null + ) + ) + } + when (termResponse) { + is WpRequestResult.Success -> { + val term = termResponse.response.data + appLogWrapper.d(AppLog.T.POSTS, "Updated $taxonomyName: ${term.name}") + val payload = RemoteTermPayload( + TermModel( + term.id.toInt(), + site.id, + term.id, + taxonomyName, + term.name, + term.slug, + term.description, + term.parent ?: 0, + term.count.toInt() + ), + site + ) + notifyTermUpdated(payload) + } + else -> { + appLogWrapper.e(AppLog.T.POSTS, "Failed updating ${term.name}: $termResponse") + val payload = RemoteTermPayload(term, site) + payload.error = TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, "") + notifyTermUpdated(payload) + } + } } - @Suppress("LongParameterList") - private inline fun handleUpdateResponse( - response: WpRequestResult, - termType: String, - term: TermModel, - site: SiteModel, - extractData: (WpRequestResult.Success) -> Any, - createTermModel: (Any) -> TermModel + private fun notifyTermUpdated( + payload: RemoteTermPayload, ) { -// when (response) { -// is WpRequestResult.Success -> { -// val data = extractData(response) -// val name = when (data) { -// is CategoryWithEditContext -> data.name -// is TagWithEditContext -> data.name -// else -> "unknown" -// } -// appLogWrapper.d(AppLog.T.POSTS, "${termType.replaceFirstChar { it.uppercase() }} updated: $name") -// val payload = RemoteTermPayload(createTermModel(data), site) -// notifyTermCreated(payload) // FluxC uses notifyTermCreated for updates -// } -// else -> { -// notifyFailedOperation( -// operation = "updating", -// termType = termType, -// term = term, -// site = site, -// errorDetails = response.toString(), -// notifier = ::notifyTermCreated -// ) -// } -// } + // FluxC uses notifyTermCreated for updates + dispatcher.dispatch(TaxonomyActionBuilder.newPushedTermAction(payload)) } fun fetchTerms(site: SiteModel, taxonomyName: String) { @@ -303,75 +251,37 @@ class TaxonomyRsApiRestClient @Inject constructor( val termsResponsePayload = when (termsResponse) { is WpRequestResult.Success -> { appLogWrapper.d(AppLog.T.POSTS, "Fetched $taxonomyName list: ${termsResponse.response.data.size}") - createTermsResponsePayload( - terms = termsResponse.response.data.map { term -> - TermModel( - term.id.toInt(), - site.id, - term.id, - taxonomyName, - term.name, - term.slug, - term.description, - term.parent ?: 0, - term.count.toInt() - ) - }, + FetchTermsResponsePayload( + TermsModel( + termsResponse.response.data.map { term -> + TermModel( + term.id.toInt(), + site.id, + term.id, + taxonomyName, + term.name, + term.slug, + term.description, + term.parent ?: 0, + term.count.toInt() + ) + }, + ), site, taxonomyName ) } else -> { appLogWrapper.e(AppLog.T.POSTS, "Fetch $termEndpointType list failed: $termsResponse") - createErrorResponsePayload(taxonomyName) + FetchTermsResponsePayload( + TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, ""), + taxonomyName + ) } } - notifyTermsFetched(termsResponsePayload) - } - - private fun notifyTermsFetched( - payload: FetchTermsResponsePayload, - ) { - dispatcher.dispatch(TaxonomyActionBuilder.newFetchedTermsAction(payload)) - } - - private fun notifyTermDeleted( - payload: RemoteTermPayload, - ) { - dispatcher.dispatch(TaxonomyActionBuilder.newDeletedTermAction(payload)) + dispatcher.dispatch(TaxonomyActionBuilder.newFetchedTermsAction(termsResponsePayload)) } - @Suppress("LongParameterList") - private fun notifyFailedOperation( - operation: String, - termType: String, - term: TermModel, - site: SiteModel, - errorDetails: String, - notifier: (RemoteTermPayload) -> Unit - ) { - appLogWrapper.e(AppLog.T.POSTS, "Failed $operation $termType: $errorDetails") - val payload = RemoteTermPayload(term, site) - payload.error = TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, "") - notifier(payload) - } - - private fun createErrorResponsePayload(taxonomyName: String): FetchTermsResponsePayload = - FetchTermsResponsePayload( - TaxonomyError(TaxonomyErrorType.GENERIC_ERROR, ""), - taxonomyName - ) - - - private fun createTermsResponsePayload( - terms: List, - site: SiteModel, - taxonomyName: String - ): FetchTermsResponsePayload = FetchTermsResponsePayload( - TermsModel(terms), - site, - taxonomyName - ) private fun TermEndpointType.toTaxonomyName(): String = when (this) { TermEndpointType.Categories -> DEFAULT_TAXONOMY_CATEGORY From 726a4d3ef859579d41563b3c8a2e92519c6b6e96 Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 13:44:27 +0200 Subject: [PATCH 06/19] Update term fix --- .../java/org/wordpress/android/fluxc/store/TaxonomyStore.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TaxonomyStore.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TaxonomyStore.java index 83980582afd0..13ca0bde49ae 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TaxonomyStore.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/TaxonomyStore.java @@ -408,8 +408,8 @@ private void handlePushTermCompleted(@NonNull RemoteTermPayload payload) { onTermUploaded.error = payload.error; emitChange(onTermUploaded); } else { - if (payload.site.isUsingWpComRestApi()) { - // The WP.COM REST API response contains the modified term, so we're already in sync with the server + if (payload.site.isUsingWpComRestApi() || payload.site.isUsingSelfHostedRestApi()) { + // The WP.COM and REST API response contains the modified term, so we're already in sync with the server // All we need to do is store it and emit OnTaxonomyChanged updateTerm(payload.term); emitChange(new OnTermUploaded(payload.term)); From 3a0639b5a4eae95896881dd06e66799f3fefe413 Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 14:07:20 +0200 Subject: [PATCH 07/19] Creating the new isHierarchical cocal field --- .../org/wordpress/android/fluxc/model/TermModel.java | 11 +++++++++++ .../rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt | 8 ++++++-- .../rest/wpcom/taxonomy/TaxonomyRestClient.java | 1 + .../network/xmlrpc/taxonomy/TaxonomyXMLRPCClient.java | 1 + .../android/fluxc/persistence/WellSqlConfig.kt | 6 +++++- 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/TermModel.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/TermModel.java index a18283d533d5..f458f8d1c538 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/TermModel.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/TermModel.java @@ -27,6 +27,7 @@ public class TermModel extends Payload implements Identifiable @Nullable @Column private String mSlug; @Nullable @Column private String mDescription; @Column private long mParentRemoteId; + @Column private boolean mIsHierarchical; @Column private int mPostCount; @Deprecated @@ -86,6 +87,7 @@ public TermModel( @Nullable String slug, @Nullable String description, long parentRemoteId, + boolean isHierarchical, int postCount) { this.mId = id; this.mLocalSiteId = localSiteId; @@ -95,6 +97,7 @@ public TermModel( this.mSlug = slug; this.mDescription = description; this.mParentRemoteId = parentRemoteId; + this.mIsHierarchical = isHierarchical; this.mPostCount = postCount; } @@ -168,6 +171,14 @@ public void setParentRemoteId(long parentRemoteId) { mParentRemoteId = parentRemoteId; } + public boolean isHierarchical() { + return mIsHierarchical; + } + + public void setIsHierarchical(boolean hierarchical) { + mIsHierarchical = hierarchical; + } + public int getPostCount() { return mPostCount; } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt index 7204a6f14aad..352f101f6a92 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt @@ -69,6 +69,7 @@ class TaxonomyRsApiRestClient @Inject constructor( term.slug, term.description, term.parentRemoteId, + term.isHierarchical, term.postCount ) notifyTermDeleted(RemoteTermPayload(termModel, site)) @@ -119,7 +120,7 @@ class TaxonomyRsApiRestClient @Inject constructor( name = term.name, description = term.description, slug = term.slug, - parent = if (term.parentRemoteId > 0) term.parentRemoteId else null + parent = if (term.isHierarchical) term.parentRemoteId else null ) ) } @@ -138,6 +139,7 @@ class TaxonomyRsApiRestClient @Inject constructor( term.slug, term.description, term.parent ?: 0, + term.parent != null, term.count.toInt() ), site @@ -186,7 +188,7 @@ class TaxonomyRsApiRestClient @Inject constructor( name = term.name, description = term.description, slug = term.slug, - parent = if (term.parentRemoteId > 0) term.parentRemoteId else null + parent = if (term.isHierarchical) term.parentRemoteId else null ) ) } @@ -204,6 +206,7 @@ class TaxonomyRsApiRestClient @Inject constructor( term.slug, term.description, term.parent ?: 0, + term.parent != null, term.count.toInt() ), site @@ -263,6 +266,7 @@ class TaxonomyRsApiRestClient @Inject constructor( term.slug, term.description, term.parent ?: 0, + term.parent != null, term.count.toInt() ) }, diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/taxonomy/TaxonomyRestClient.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/taxonomy/TaxonomyRestClient.java index 3e4f79b0e524..8e8945e38c09 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/taxonomy/TaxonomyRestClient.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/taxonomy/TaxonomyRestClient.java @@ -174,6 +174,7 @@ private TermModel termResponseToTermModel( from.slug, StringEscapeUtils.unescapeHtml4(from.description), from.parent, + true, // this is only applicable for the new Rest API from.post_count ); } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/xmlrpc/taxonomy/TaxonomyXMLRPCClient.java b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/xmlrpc/taxonomy/TaxonomyXMLRPCClient.java index 67bf4d2a69d5..f282b98a0ffe 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/xmlrpc/taxonomy/TaxonomyXMLRPCClient.java +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/xmlrpc/taxonomy/TaxonomyXMLRPCClient.java @@ -239,6 +239,7 @@ private TermModel termResponseObjectToTermModel(@NonNull Object termObject, @Non MapUtils.getMapStr(termMap, "slug"), StringEscapeUtils.unescapeHtml4(MapUtils.getMapStr(termMap, "description")), MapUtils.getMapLong(termMap, "parent"), + true, // this is only applicable for the new Rest API MapUtils.getMapInt(termMap, "count", 0) ); } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt index 380f9a7e4bba..a8f18b82a34f 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt @@ -41,7 +41,7 @@ open class WellSqlConfig : DefaultWellConfig { annotation class AddOn override fun getDbVersion(): Int { - return 210 + return 211 } override fun getDbName(): String { @@ -2085,6 +2085,10 @@ open class WellSqlConfig : DefaultWellConfig { db.execSQL("ALTER TABLE MediaModel ADD FILE_SIZE INTEGER") db.execSQL("ALTER TABLE MediaModel ADD FILE_SIZE_FORMATTED TEXT") } + + 210 -> { + db.execSQL("ALTER TABLE TermModel ADD IS_HIERARCHICAL BOOLEAN") + } } } db.setTransactionSuccessful() From 6bce343d007cfd25ddd0a3890d2600cc2339d050 Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 14:15:44 +0200 Subject: [PATCH 08/19] Parent fix --- .../java/org/wordpress/android/ui/posts/EditCategoryUseCase.kt | 1 + .../network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditCategoryUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditCategoryUseCase.kt index b8349d2a3a6f..3f1957e8daf9 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditCategoryUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditCategoryUseCase.kt @@ -29,6 +29,7 @@ class EditCategoryUseCase @Inject constructor( existingCategory.slug, existingCategory.description, parentCategoryId, + existingCategory.isHierarchical, existingCategory.postCount ) val payload = RemoteTermPayload(editedCategory, siteModel) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt index 352f101f6a92..3a73accee310 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt @@ -120,7 +120,8 @@ class TaxonomyRsApiRestClient @Inject constructor( name = term.name, description = term.description, slug = term.slug, - parent = if (term.isHierarchical) term.parentRemoteId else null + // Right now, this is the only way we have to know if it's hierarchical + parent = if (termEndpointType == TermEndpointType.Categories) term.parentRemoteId else null ) ) } From c39d8fbb1f21f2f9863f7dc6ca5017007a895aed Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 14:25:51 +0200 Subject: [PATCH 09/19] Fixing tests --- .../taxonomy/TaxonomyRsApiRestClientTest.kt | 238 +++++++++--------- .../fluxc/taxonomy/TaxonomyTestUtils.java | 1 + 2 files changed, 113 insertions(+), 126 deletions(-) diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClientTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClientTest.kt index 54d3c7fc8b70..c19a6a65fa4f 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClientTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClientTest.kt @@ -31,18 +31,12 @@ import org.wordpress.android.fluxc.store.TaxonomyStore.TaxonomyErrorType import org.wordpress.android.fluxc.utils.AppLogWrapper import rs.wordpress.api.kotlin.WpApiClient import rs.wordpress.api.kotlin.WpRequestResult -import uniffi.wp_api.CategoryDeleteResponse -import uniffi.wp_api.CategoryWithEditContext -import uniffi.wp_api.CategoriesRequestCreateResponse -import uniffi.wp_api.CategoriesRequestDeleteResponse -import uniffi.wp_api.CategoriesRequestListWithEditContextResponse -import uniffi.wp_api.CategoriesRequestUpdateResponse -import uniffi.wp_api.TagDeleteResponse -import uniffi.wp_api.TagWithEditContext -import uniffi.wp_api.TagsRequestCreateResponse -import uniffi.wp_api.TagsRequestDeleteResponse -import uniffi.wp_api.TagsRequestListWithEditContextResponse -import uniffi.wp_api.TagsRequestUpdateResponse +import uniffi.wp_api.AnyTermWithEditContext +import uniffi.wp_api.TermDeleteResponse +import uniffi.wp_api.TermsRequestCreateResponse +import uniffi.wp_api.TermsRequestDeleteResponse +import uniffi.wp_api.TermsRequestListWithEditContextResponse +import uniffi.wp_api.TermsRequestUpdateResponse import uniffi.wp_api.TaxonomyType import uniffi.wp_api.WpNetworkHeaderMap @@ -66,7 +60,7 @@ class TaxonomyRsApiRestClientTest { url = "https://test.wordpress.com" } - private val testTermModel = TermModel( + private val testCategoryTermModel = TermModel( 1, // id 123, // localSiteId 2L, // remoteTermId @@ -75,6 +69,7 @@ class TaxonomyRsApiRestClientTest { "test-category", // slug "Test category description", // description 0L, // parentRemoteId + true, // isHierarchical 0 // postCount ) @@ -87,6 +82,7 @@ class TaxonomyRsApiRestClientTest { "test-tag", // slug "Test tag description", // description 0L, // parentRemoteId + false, // isHierarchical 0 // postCount ) @@ -137,24 +133,24 @@ class TaxonomyRsApiRestClientTest { @Test fun `fetchTerms tags with success response dispatches success action`() = runTest { - val tagWithEditContext = listOf( - createTestTagWithEditContext(), - createTestTagWithEditContext() + val AnyTermWithEditContext = listOf( + createTestAnyTermWithEditContext(), + createTestAnyTermWithEditContext() ) // Create the correct response structure following the MediaRSApiRestClientTest pattern - val tagResponse = TagsRequestListWithEditContextResponse( - tagWithEditContext, + val tagResponse = TermsRequestListWithEditContextResponse( + AnyTermWithEditContext, mock(), null, null ) - val successResponse: WpRequestResult = WpRequestResult.Success( + val successResponse: WpRequestResult = WpRequestResult.Success( response = tagResponse ) - whenever(wpApiClient.request(any())).thenReturn(successResponse) + whenever(wpApiClient.request(any())).thenReturn(successResponse) taxonomyClient.fetchTerms(testSite, testTagTaxonomyName) @@ -198,24 +194,24 @@ class TaxonomyRsApiRestClientTest { @Test fun `fetchTerms categories with success response dispatches success action`() = runTest { - val categoryWithEditContext = listOf( - createTestCategoryWithEditContext(), - createTestCategoryWithEditContext() + val AnyTermWithEditContext = listOf( + createTestAnyTermWithEditContext(), + createTestAnyTermWithEditContext() ) // Create the correct response structure following the MediaRSApiRestClientTest pattern - val categoryResponse = CategoriesRequestListWithEditContextResponse( - categoryWithEditContext, + val categoryResponse = TermsRequestListWithEditContextResponse( + AnyTermWithEditContext, mock(), null, null ) - val successResponse: WpRequestResult = WpRequestResult.Success( + val successResponse: WpRequestResult = WpRequestResult.Success( response = categoryResponse ) - whenever(wpApiClient.request(any())).thenReturn(successResponse) + whenever(wpApiClient.request(any())).thenReturn(successResponse) taxonomyClient.fetchTerms(testSite, testCategoryTaxonomyName) @@ -243,7 +239,7 @@ class TaxonomyRsApiRestClientTest { whenever(wpApiClient.request(any())).thenReturn(errorResponse) - taxonomyClient.createTerm(testSite, testTermModel) + taxonomyClient.createTerm(testSite, testCategoryTermModel) // Verify dispatcher was called with error action val actionCaptor = ArgumentCaptor.forClass(Action::class.java) @@ -253,28 +249,28 @@ class TaxonomyRsApiRestClientTest { val payload = capturedAction.payload as RemoteTermPayload assertEquals(capturedAction.type, TaxonomyAction.PUSHED_TERM) assertEquals(testSite, payload.site) - assertEquals(testTermModel, payload.term) + assertEquals(testCategoryTermModel, payload.term) assertNotNull(payload.error) assertEquals(TaxonomyErrorType.GENERIC_ERROR, payload.error?.type) } @Test fun `createTerm category with success response dispatches success action`() = runTest { - val categoryWithEditContext = createTestCategoryWithEditContext() + val AnyTermWithEditContext = createTestAnyTermWithEditContext() // Create the correct response structure following the MediaRSApiRestClientTest pattern - val categoryResponse = CategoriesRequestCreateResponse( - categoryWithEditContext, + val categoryResponse = TermsRequestCreateResponse( + AnyTermWithEditContext, mock() ) - val successResponse: WpRequestResult = WpRequestResult.Success( + val successResponse: WpRequestResult = WpRequestResult.Success( response = categoryResponse ) - whenever(wpApiClient.request(any())).thenReturn(successResponse) + whenever(wpApiClient.request(any())).thenReturn(successResponse) - taxonomyClient.createTerm(testSite, testTermModel) + taxonomyClient.createTerm(testSite, testCategoryTermModel) // Verify dispatcher was called with success action val actionCaptor = ArgumentCaptor.forClass(Action::class.java) @@ -286,14 +282,14 @@ class TaxonomyRsApiRestClientTest { assertEquals(testSite, payload.site) assertNotNull(payload.term) // Verify the created term has the correct properties - assertEquals(categoryWithEditContext.id.toInt(), payload.term.id) + assertEquals(AnyTermWithEditContext.id.toInt(), payload.term.id) assertEquals(testSite.id, payload.term.localSiteId) - assertEquals(categoryWithEditContext.id, payload.term.remoteTermId) + assertEquals(AnyTermWithEditContext.id, payload.term.remoteTermId) assertEquals(testCategoryTaxonomyName, payload.term.taxonomy) - assertEquals(categoryWithEditContext.name, payload.term.name) - assertEquals(categoryWithEditContext.slug, payload.term.slug) - assertEquals(categoryWithEditContext.description, payload.term.description) - assertEquals(categoryWithEditContext.count.toInt(), payload.term.postCount) + assertEquals(AnyTermWithEditContext.name, payload.term.name) + assertEquals(AnyTermWithEditContext.slug, payload.term.slug) + assertEquals(AnyTermWithEditContext.description, payload.term.description) + assertEquals(AnyTermWithEditContext.count.toInt(), payload.term.postCount) assertNull(payload.error) } @@ -324,19 +320,19 @@ class TaxonomyRsApiRestClientTest { @Test fun `createTerm tag with success response dispatches success action`() = runTest { - val tagWithEditContext = createTestTagWithEditContext() + val AnyTermWithEditContext = createTestAnyTermWithEditContext() // Create the correct response structure following the MediaRSApiRestClientTest pattern - val tagResponse = TagsRequestCreateResponse( - tagWithEditContext, + val tagResponse = TermsRequestCreateResponse( + AnyTermWithEditContext, mock() ) - val successResponse: WpRequestResult = WpRequestResult.Success( + val successResponse: WpRequestResult = WpRequestResult.Success( response = tagResponse ) - whenever(wpApiClient.request(any())).thenReturn(successResponse) + whenever(wpApiClient.request(any())).thenReturn(successResponse) taxonomyClient.createTerm(testSite, testTagTermModel) @@ -350,14 +346,14 @@ class TaxonomyRsApiRestClientTest { assertEquals(testSite, payload.site) assertNotNull(payload.term) // Verify the created term has the correct properties - assertEquals(tagWithEditContext.id.toInt(), payload.term.id) + assertEquals(AnyTermWithEditContext.id.toInt(), payload.term.id) assertEquals(testSite.id, payload.term.localSiteId) - assertEquals(tagWithEditContext.id, payload.term.remoteTermId) + assertEquals(AnyTermWithEditContext.id, payload.term.remoteTermId) assertEquals(testTagTaxonomyName, payload.term.taxonomy) - assertEquals(tagWithEditContext.name, payload.term.name) - assertEquals(tagWithEditContext.slug, payload.term.slug) - assertEquals(tagWithEditContext.description, payload.term.description) - assertEquals(tagWithEditContext.count.toInt(), payload.term.postCount) + assertEquals(AnyTermWithEditContext.name, payload.term.name) + assertEquals(AnyTermWithEditContext.slug, payload.term.slug) + assertEquals(AnyTermWithEditContext.description, payload.term.description) + assertEquals(AnyTermWithEditContext.count.toInt(), payload.term.postCount) assertNull(payload.error) } @@ -371,7 +367,7 @@ class TaxonomyRsApiRestClientTest { whenever(wpApiClient.request(any())).thenReturn(errorResponse) - taxonomyClient.deleteTerm(testSite, testTermModel) + taxonomyClient.deleteTerm(testSite, testCategoryTermModel) // Verify dispatcher was called with error action val actionCaptor = ArgumentCaptor.forClass(Action::class.java) @@ -381,7 +377,7 @@ class TaxonomyRsApiRestClientTest { val payload = capturedAction.payload as RemoteTermPayload assertEquals(capturedAction.type, TaxonomyAction.DELETED_TERM) assertEquals(testSite, payload.site) - assertEquals(testTermModel, payload.term) + assertEquals(testCategoryTermModel, payload.term) assertNotNull(payload.error) assertEquals(TaxonomyErrorType.GENERIC_ERROR, payload.error?.type) } @@ -391,18 +387,18 @@ class TaxonomyRsApiRestClientTest { val categoryDeleteData = createTestCategoryDeleteData(deleted = true) // Create the correct response structure following the MediaRsApiRestClientTest pattern - val categoryResponse = CategoriesRequestDeleteResponse( + val categoryResponse = TermsRequestDeleteResponse( categoryDeleteData, mock() ) - val successResponse: WpRequestResult = WpRequestResult.Success( + val successResponse: WpRequestResult = WpRequestResult.Success( response = categoryResponse ) - whenever(wpApiClient.request(any())).thenReturn(successResponse) + whenever(wpApiClient.request(any())).thenReturn(successResponse) - taxonomyClient.deleteTerm(testSite, testTermModel) + taxonomyClient.deleteTerm(testSite, testCategoryTermModel) // Verify dispatcher was called with success action val actionCaptor = ArgumentCaptor.forClass(Action::class.java) @@ -414,14 +410,14 @@ class TaxonomyRsApiRestClientTest { assertEquals(testSite, payload.site) assertNotNull(payload.term) // Verify the deleted term has the correct properties - assertEquals(testTermModel.id, payload.term.id) + assertEquals(testCategoryTermModel.id, payload.term.id) assertEquals(testSite.id, payload.term.localSiteId) - assertEquals(testTermModel.id.toLong(), payload.term.remoteTermId) + assertEquals(testCategoryTermModel.id.toLong(), payload.term.remoteTermId) assertEquals(testCategoryTaxonomyName, payload.term.taxonomy) - assertEquals(testTermModel.name, payload.term.name) - assertEquals(testTermModel.slug, payload.term.slug) - assertEquals(testTermModel.description, payload.term.description) - assertEquals(testTermModel.postCount, payload.term.postCount) + assertEquals(testCategoryTermModel.name, payload.term.name) + assertEquals(testCategoryTermModel.slug, payload.term.slug) + assertEquals(testCategoryTermModel.description, payload.term.description) + assertEquals(testCategoryTermModel.postCount, payload.term.postCount) assertNull(payload.error) } @@ -430,18 +426,18 @@ class TaxonomyRsApiRestClientTest { val categoryDeleteData = createTestCategoryDeleteData(deleted = false) // Create the correct response structure with deleted = false - val categoryResponse = CategoriesRequestDeleteResponse( + val categoryResponse = TermsRequestDeleteResponse( categoryDeleteData, mock() ) - val successResponse: WpRequestResult = WpRequestResult.Success( + val successResponse: WpRequestResult = WpRequestResult.Success( response = categoryResponse ) - whenever(wpApiClient.request(any())).thenReturn(successResponse) + whenever(wpApiClient.request(any())).thenReturn(successResponse) - taxonomyClient.deleteTerm(testSite, testTermModel) + taxonomyClient.deleteTerm(testSite, testCategoryTermModel) // Verify dispatcher was called with error action val actionCaptor = ArgumentCaptor.forClass(Action::class.java) @@ -451,7 +447,7 @@ class TaxonomyRsApiRestClientTest { val payload = capturedAction.payload as RemoteTermPayload assertEquals(capturedAction.type, TaxonomyAction.DELETED_TERM) assertEquals(testSite, payload.site) - assertEquals(testTermModel, payload.term) + assertEquals(testCategoryTermModel, payload.term) assertNotNull(payload.error) assertEquals(TaxonomyErrorType.GENERIC_ERROR, payload.error?.type) } @@ -486,16 +482,16 @@ class TaxonomyRsApiRestClientTest { val tagDeleteData = createTestTagDeleteData(deleted = true) // Create the correct response structure following the MediaRsApiRestClientTest pattern - val tagResponse = TagsRequestDeleteResponse( + val tagResponse = TermsRequestDeleteResponse( tagDeleteData, mock() ) - val successResponse: WpRequestResult = WpRequestResult.Success( + val successResponse: WpRequestResult = WpRequestResult.Success( response = tagResponse ) - whenever(wpApiClient.request(any())).thenReturn(successResponse) + whenever(wpApiClient.request(any())).thenReturn(successResponse) taxonomyClient.deleteTerm(testSite, testTagTermModel) @@ -525,16 +521,16 @@ class TaxonomyRsApiRestClientTest { val tagDeleteData = createTestTagDeleteData(deleted = false) // Create the correct response structure with deleted = false - val tagResponse = TagsRequestDeleteResponse( + val tagResponse = TermsRequestDeleteResponse( tagDeleteData, mock() ) - val successResponse: WpRequestResult = WpRequestResult.Success( + val successResponse: WpRequestResult = WpRequestResult.Success( response = tagResponse ) - whenever(wpApiClient.request(any())).thenReturn(successResponse) + whenever(wpApiClient.request(any())).thenReturn(successResponse) taxonomyClient.deleteTerm(testSite, testTagTermModel) @@ -560,7 +556,7 @@ class TaxonomyRsApiRestClientTest { whenever(wpApiClient.request(any())).thenReturn(errorResponse) - taxonomyClient.updateTerm(testSite, testTermModel) + taxonomyClient.updateTerm(testSite, testCategoryTermModel) val actionCaptor = ArgumentCaptor.forClass(Action::class.java) verify(dispatcher).dispatch(actionCaptor.capture()) @@ -569,27 +565,27 @@ class TaxonomyRsApiRestClientTest { val payload = capturedAction.payload as RemoteTermPayload assertEquals(capturedAction.type, TaxonomyAction.PUSHED_TERM) assertEquals(testSite, payload.site) - assertEquals(testTermModel, payload.term) + assertEquals(testCategoryTermModel, payload.term) assertNotNull(payload.error) assertEquals(TaxonomyErrorType.GENERIC_ERROR, payload.error?.type) } @Test fun `updateTerm category with success response dispatches success action`() = runTest { - val categoryWithEditContext = createTestCategoryWithEditContext() + val AnyTermWithEditContext = createTestAnyTermWithEditContext() - val categoryResponse = CategoriesRequestUpdateResponse( - categoryWithEditContext, + val categoryResponse = TermsRequestUpdateResponse( + AnyTermWithEditContext, mock() ) - val successResponse: WpRequestResult = WpRequestResult.Success( + val successResponse: WpRequestResult = WpRequestResult.Success( response = categoryResponse ) - whenever(wpApiClient.request(any())).thenReturn(successResponse) + whenever(wpApiClient.request(any())).thenReturn(successResponse) - taxonomyClient.updateTerm(testSite, testTermModel) + taxonomyClient.updateTerm(testSite, testCategoryTermModel) val actionCaptor = ArgumentCaptor.forClass(Action::class.java) verify(dispatcher).dispatch(actionCaptor.capture()) @@ -599,29 +595,30 @@ class TaxonomyRsApiRestClientTest { assertEquals(capturedAction.type, TaxonomyAction.PUSHED_TERM) assertEquals(testSite, payload.site) assertNotNull(payload.term) - assertEquals(categoryWithEditContext.id.toInt(), payload.term.id) + assertEquals(AnyTermWithEditContext.id.toInt(), payload.term.id) assertEquals(testSite.id, payload.term.localSiteId) - assertEquals(categoryWithEditContext.id, payload.term.remoteTermId) + assertEquals(AnyTermWithEditContext.id, payload.term.remoteTermId) assertEquals(testCategoryTaxonomyName, payload.term.taxonomy) - assertEquals(categoryWithEditContext.name, payload.term.name) - assertEquals(categoryWithEditContext.slug, payload.term.slug) - assertEquals(categoryWithEditContext.description, payload.term.description) - assertEquals(categoryWithEditContext.count.toInt(), payload.term.postCount) + assertEquals(AnyTermWithEditContext.name, payload.term.name) + assertEquals(AnyTermWithEditContext.slug, payload.term.slug) + assertEquals(AnyTermWithEditContext.description, payload.term.description) + assertEquals(AnyTermWithEditContext.count.toInt(), payload.term.postCount) assertNull(payload.error) } @Test fun `updateTerm category with invalid id dispatches error action`() = runTest { val invalidTermModel = TermModel( - testTermModel.id, - testTermModel.localSiteId, + testCategoryTermModel.id, + testCategoryTermModel.localSiteId, -1L, // invalid remoteTermId - testTermModel.taxonomy, - testTermModel.name, - testTermModel.slug, - testTermModel.description, - testTermModel.parentRemoteId, - testTermModel.postCount + testCategoryTermModel.taxonomy, + testCategoryTermModel.name, + testCategoryTermModel.slug, + testCategoryTermModel.description, + testCategoryTermModel.parentRemoteId, + testCategoryTermModel.isHierarchical, + testCategoryTermModel.postCount ) taxonomyClient.updateTerm(testSite, invalidTermModel) @@ -663,18 +660,18 @@ class TaxonomyRsApiRestClientTest { @Test fun `updateTerm tag with success response dispatches success action`() = runTest { - val tagWithEditContext = createTestTagWithEditContext() + val AnyTermWithEditContext = createTestAnyTermWithEditContext() - val tagResponse = TagsRequestUpdateResponse( - tagWithEditContext, + val tagResponse = TermsRequestUpdateResponse( + AnyTermWithEditContext, mock() ) - val successResponse: WpRequestResult = WpRequestResult.Success( + val successResponse: WpRequestResult = WpRequestResult.Success( response = tagResponse ) - whenever(wpApiClient.request(any())).thenReturn(successResponse) + whenever(wpApiClient.request(any())).thenReturn(successResponse) taxonomyClient.updateTerm(testSite, testTagTermModel) @@ -686,14 +683,14 @@ class TaxonomyRsApiRestClientTest { assertEquals(capturedAction.type, TaxonomyAction.PUSHED_TERM) assertEquals(testSite, payload.site) assertNotNull(payload.term) - assertEquals(tagWithEditContext.id.toInt(), payload.term.id) + assertEquals(AnyTermWithEditContext.id.toInt(), payload.term.id) assertEquals(testSite.id, payload.term.localSiteId) - assertEquals(tagWithEditContext.id, payload.term.remoteTermId) + assertEquals(AnyTermWithEditContext.id, payload.term.remoteTermId) assertEquals(testTagTaxonomyName, payload.term.taxonomy) - assertEquals(tagWithEditContext.name, payload.term.name) - assertEquals(tagWithEditContext.slug, payload.term.slug) - assertEquals(tagWithEditContext.description, payload.term.description) - assertEquals(tagWithEditContext.count.toInt(), payload.term.postCount) + assertEquals(AnyTermWithEditContext.name, payload.term.name) + assertEquals(AnyTermWithEditContext.slug, payload.term.slug) + assertEquals(AnyTermWithEditContext.description, payload.term.description) + assertEquals(AnyTermWithEditContext.count.toInt(), payload.term.postCount) assertNull(payload.error) } @@ -708,6 +705,7 @@ class TaxonomyRsApiRestClientTest { testTagTermModel.slug, testTagTermModel.description, testTagTermModel.parentRemoteId, + testTagTermModel.isHierarchical, testTagTermModel.postCount ) @@ -725,16 +723,16 @@ class TaxonomyRsApiRestClientTest { assertEquals(TaxonomyErrorType.GENERIC_ERROR, payload.error?.type) } - private fun createTestCategoryDeleteData(deleted: Boolean): CategoryDeleteResponse { - return CategoryDeleteResponse(deleted, createTestCategoryWithEditContext()) + private fun createTestCategoryDeleteData(deleted: Boolean): TermDeleteResponse { + return TermDeleteResponse(deleted, createTestAnyTermWithEditContext()) } - private fun createTestTagDeleteData(deleted: Boolean): TagDeleteResponse { - return TagDeleteResponse(deleted, createTestTagWithEditContext()) + private fun createTestTagDeleteData(deleted: Boolean): TermDeleteResponse { + return TermDeleteResponse(deleted, createTestAnyTermWithEditContext()) } - private fun createTestCategoryWithEditContext(): CategoryWithEditContext { - return CategoryWithEditContext( + private fun createTestAnyTermWithEditContext(): AnyTermWithEditContext { + return AnyTermWithEditContext( id = 2L, count = 3L, description = "Test category description", @@ -745,17 +743,5 @@ class TaxonomyRsApiRestClientTest { parent = 0L ) } - - private fun createTestTagWithEditContext(): TagWithEditContext { - return TagWithEditContext( - id = 1L, - count = 5L, - description = "Test tag description", - link = "https://example.com/tag/test", - name = "Test Tag", - slug = "test-tag", - taxonomy = TaxonomyType.PostTag - ) - } } diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/taxonomy/TaxonomyTestUtils.java b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/taxonomy/TaxonomyTestUtils.java index cf4c54b16d46..fb9433e0ad33 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/taxonomy/TaxonomyTestUtils.java +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/taxonomy/TaxonomyTestUtils.java @@ -21,6 +21,7 @@ private static TermModel generateSampleTerm(@NonNull String taxonomy) { "travel", "Post about travelling", 0, + false, 0 ); } From 630a7b1681c3a9cc748f4ff80295450a81b0b805 Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 14:27:25 +0200 Subject: [PATCH 10/19] detekt --- .../wpapi/taxonomy/TaxonomyRsApiRestClient.kt | 5 +- .../taxonomy/TaxonomyRsApiRestClientTest.kt | 72 +++++++++---------- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt index 3a73accee310..99c6aa358dfd 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt @@ -58,7 +58,10 @@ class TaxonomyRsApiRestClient @Inject constructor( } when (termResponse) { is WpRequestResult.Success -> { - appLogWrapper.d(AppLog.T.POSTS, "Deleting $taxonomyName: ${term.name} - ${termResponse.response.data.deleted}") + appLogWrapper.d( + AppLog.T.POSTS, + "Deleting $taxonomyName: ${term.name} - ${termResponse.response.data.deleted}" + ) if (termResponse.response.data.deleted) { val termModel = TermModel( term.id, diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClientTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClientTest.kt index c19a6a65fa4f..45fdcc29efaa 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClientTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClientTest.kt @@ -133,14 +133,14 @@ class TaxonomyRsApiRestClientTest { @Test fun `fetchTerms tags with success response dispatches success action`() = runTest { - val AnyTermWithEditContext = listOf( + val anyTermWithEditContext = listOf( createTestAnyTermWithEditContext(), createTestAnyTermWithEditContext() ) // Create the correct response structure following the MediaRSApiRestClientTest pattern val tagResponse = TermsRequestListWithEditContextResponse( - AnyTermWithEditContext, + anyTermWithEditContext, mock(), null, null @@ -194,14 +194,14 @@ class TaxonomyRsApiRestClientTest { @Test fun `fetchTerms categories with success response dispatches success action`() = runTest { - val AnyTermWithEditContext = listOf( + val anyTermWithEditContext = listOf( createTestAnyTermWithEditContext(), createTestAnyTermWithEditContext() ) // Create the correct response structure following the MediaRSApiRestClientTest pattern val categoryResponse = TermsRequestListWithEditContextResponse( - AnyTermWithEditContext, + anyTermWithEditContext, mock(), null, null @@ -256,11 +256,11 @@ class TaxonomyRsApiRestClientTest { @Test fun `createTerm category with success response dispatches success action`() = runTest { - val AnyTermWithEditContext = createTestAnyTermWithEditContext() + val anyTermWithEditContext = createTestAnyTermWithEditContext() // Create the correct response structure following the MediaRSApiRestClientTest pattern val categoryResponse = TermsRequestCreateResponse( - AnyTermWithEditContext, + anyTermWithEditContext, mock() ) @@ -282,14 +282,14 @@ class TaxonomyRsApiRestClientTest { assertEquals(testSite, payload.site) assertNotNull(payload.term) // Verify the created term has the correct properties - assertEquals(AnyTermWithEditContext.id.toInt(), payload.term.id) + assertEquals(anyTermWithEditContext.id.toInt(), payload.term.id) assertEquals(testSite.id, payload.term.localSiteId) - assertEquals(AnyTermWithEditContext.id, payload.term.remoteTermId) + assertEquals(anyTermWithEditContext.id, payload.term.remoteTermId) assertEquals(testCategoryTaxonomyName, payload.term.taxonomy) - assertEquals(AnyTermWithEditContext.name, payload.term.name) - assertEquals(AnyTermWithEditContext.slug, payload.term.slug) - assertEquals(AnyTermWithEditContext.description, payload.term.description) - assertEquals(AnyTermWithEditContext.count.toInt(), payload.term.postCount) + assertEquals(anyTermWithEditContext.name, payload.term.name) + assertEquals(anyTermWithEditContext.slug, payload.term.slug) + assertEquals(anyTermWithEditContext.description, payload.term.description) + assertEquals(anyTermWithEditContext.count.toInt(), payload.term.postCount) assertNull(payload.error) } @@ -320,11 +320,11 @@ class TaxonomyRsApiRestClientTest { @Test fun `createTerm tag with success response dispatches success action`() = runTest { - val AnyTermWithEditContext = createTestAnyTermWithEditContext() + val anyTermWithEditContext = createTestAnyTermWithEditContext() // Create the correct response structure following the MediaRSApiRestClientTest pattern val tagResponse = TermsRequestCreateResponse( - AnyTermWithEditContext, + anyTermWithEditContext, mock() ) @@ -346,14 +346,14 @@ class TaxonomyRsApiRestClientTest { assertEquals(testSite, payload.site) assertNotNull(payload.term) // Verify the created term has the correct properties - assertEquals(AnyTermWithEditContext.id.toInt(), payload.term.id) + assertEquals(anyTermWithEditContext.id.toInt(), payload.term.id) assertEquals(testSite.id, payload.term.localSiteId) - assertEquals(AnyTermWithEditContext.id, payload.term.remoteTermId) + assertEquals(anyTermWithEditContext.id, payload.term.remoteTermId) assertEquals(testTagTaxonomyName, payload.term.taxonomy) - assertEquals(AnyTermWithEditContext.name, payload.term.name) - assertEquals(AnyTermWithEditContext.slug, payload.term.slug) - assertEquals(AnyTermWithEditContext.description, payload.term.description) - assertEquals(AnyTermWithEditContext.count.toInt(), payload.term.postCount) + assertEquals(anyTermWithEditContext.name, payload.term.name) + assertEquals(anyTermWithEditContext.slug, payload.term.slug) + assertEquals(anyTermWithEditContext.description, payload.term.description) + assertEquals(anyTermWithEditContext.count.toInt(), payload.term.postCount) assertNull(payload.error) } @@ -572,10 +572,10 @@ class TaxonomyRsApiRestClientTest { @Test fun `updateTerm category with success response dispatches success action`() = runTest { - val AnyTermWithEditContext = createTestAnyTermWithEditContext() + val anyTermWithEditContext = createTestAnyTermWithEditContext() val categoryResponse = TermsRequestUpdateResponse( - AnyTermWithEditContext, + anyTermWithEditContext, mock() ) @@ -595,14 +595,14 @@ class TaxonomyRsApiRestClientTest { assertEquals(capturedAction.type, TaxonomyAction.PUSHED_TERM) assertEquals(testSite, payload.site) assertNotNull(payload.term) - assertEquals(AnyTermWithEditContext.id.toInt(), payload.term.id) + assertEquals(anyTermWithEditContext.id.toInt(), payload.term.id) assertEquals(testSite.id, payload.term.localSiteId) - assertEquals(AnyTermWithEditContext.id, payload.term.remoteTermId) + assertEquals(anyTermWithEditContext.id, payload.term.remoteTermId) assertEquals(testCategoryTaxonomyName, payload.term.taxonomy) - assertEquals(AnyTermWithEditContext.name, payload.term.name) - assertEquals(AnyTermWithEditContext.slug, payload.term.slug) - assertEquals(AnyTermWithEditContext.description, payload.term.description) - assertEquals(AnyTermWithEditContext.count.toInt(), payload.term.postCount) + assertEquals(anyTermWithEditContext.name, payload.term.name) + assertEquals(anyTermWithEditContext.slug, payload.term.slug) + assertEquals(anyTermWithEditContext.description, payload.term.description) + assertEquals(anyTermWithEditContext.count.toInt(), payload.term.postCount) assertNull(payload.error) } @@ -660,10 +660,10 @@ class TaxonomyRsApiRestClientTest { @Test fun `updateTerm tag with success response dispatches success action`() = runTest { - val AnyTermWithEditContext = createTestAnyTermWithEditContext() + val anyTermWithEditContext = createTestAnyTermWithEditContext() val tagResponse = TermsRequestUpdateResponse( - AnyTermWithEditContext, + anyTermWithEditContext, mock() ) @@ -683,14 +683,14 @@ class TaxonomyRsApiRestClientTest { assertEquals(capturedAction.type, TaxonomyAction.PUSHED_TERM) assertEquals(testSite, payload.site) assertNotNull(payload.term) - assertEquals(AnyTermWithEditContext.id.toInt(), payload.term.id) + assertEquals(anyTermWithEditContext.id.toInt(), payload.term.id) assertEquals(testSite.id, payload.term.localSiteId) - assertEquals(AnyTermWithEditContext.id, payload.term.remoteTermId) + assertEquals(anyTermWithEditContext.id, payload.term.remoteTermId) assertEquals(testTagTaxonomyName, payload.term.taxonomy) - assertEquals(AnyTermWithEditContext.name, payload.term.name) - assertEquals(AnyTermWithEditContext.slug, payload.term.slug) - assertEquals(AnyTermWithEditContext.description, payload.term.description) - assertEquals(AnyTermWithEditContext.count.toInt(), payload.term.postCount) + assertEquals(anyTermWithEditContext.name, payload.term.name) + assertEquals(anyTermWithEditContext.slug, payload.term.slug) + assertEquals(anyTermWithEditContext.description, payload.term.description) + assertEquals(anyTermWithEditContext.count.toInt(), payload.term.postCount) assertNull(payload.error) } From 10b8ab29554bbe2486e23ed090acf8b2b34d5b0f Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 14:32:41 +0200 Subject: [PATCH 11/19] Minor fix --- .../network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt index 99c6aa358dfd..38332b44ccf9 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpapi/taxonomy/TaxonomyRsApiRestClient.kt @@ -52,7 +52,7 @@ class TaxonomyRsApiRestClient @Inject constructor( val taxonomyName = termEndpointType.toTaxonomyName() val termResponse = client.request { requestBuilder -> requestBuilder.terms().delete( - termEndpointType = TermEndpointType.Tags, + termEndpointType = termEndpointType, termId = term.id.toLong() ) } From 03b8a9173aba6e795f47799f00b1ffb55e99c6f3 Mon Sep 17 00:00:00 2001 From: adalpari Date: Wed, 1 Oct 2025 14:51:28 +0200 Subject: [PATCH 12/19] Fixing tests --- .../posts/prepublishing/PrepublishingCategoriesViewModelTest.kt | 1 + .../ui/prefs/categories/detail/CategoryDetailViewModelTest.kt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/posts/prepublishing/PrepublishingCategoriesViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/posts/prepublishing/PrepublishingCategoriesViewModelTest.kt index ceacd9a7e49f..7a2867585270 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/posts/prepublishing/PrepublishingCategoriesViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/posts/prepublishing/PrepublishingCategoriesViewModelTest.kt @@ -356,6 +356,7 @@ class PrepublishingCategoriesViewModelTest : BaseUnitTest() { "cars", null, 0, + true, 0 ) } diff --git a/WordPress/src/test/java/org/wordpress/android/ui/prefs/categories/detail/CategoryDetailViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/prefs/categories/detail/CategoryDetailViewModelTest.kt index 04f179b8e63b..18ac5979c76b 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/prefs/categories/detail/CategoryDetailViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/prefs/categories/detail/CategoryDetailViewModelTest.kt @@ -339,6 +339,7 @@ class CategoryDetailViewModelTest : BaseUnitTest() { "animals", null, 0, + true, 0 ) } @@ -353,6 +354,7 @@ class CategoryDetailViewModelTest : BaseUnitTest() { "dog", null, 1, + true, 0 ) } From 7347ee27ca5b2a8326a823d1a57eaf18a7526f52 Mon Sep 17 00:00:00 2001 From: adalpari Date: Thu, 2 Oct 2025 12:49:43 +0200 Subject: [PATCH 13/19] Adding the taxonomies menu view model --- .../taxonomies/TaxonomiesNavMenuViewModel.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt new file mode 100644 index 000000000000..28ec00dcc262 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt @@ -0,0 +1,41 @@ +package org.wordpress.android.ui.prefs.taxonomies + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider +import org.wordpress.android.fluxc.utils.AppLogWrapper +import org.wordpress.android.util.AppLog +import rs.wordpress.api.kotlin.WpRequestResult +import uniffi.wp_api.TaxonomyListParams +import javax.inject.Inject + +class TaxonomiesNavMenuViewModel @Inject constructor( + private val wpApiClientProvider: WpApiClientProvider, + private val appLogWrapper: AppLogWrapper, +) : ViewModel() { + + fun fetchTaxonomies(site: SiteModel) { + viewModelScope.launch { + val client = wpApiClientProvider.getWpApiClient(site) + val response = client.request { requestBuilder -> + requestBuilder.taxonomies().listWithEditContext(TaxonomyListParams()) + } + when (response) { + is WpRequestResult.Success -> { + val list = response.response.data + appLogWrapper.d(AppLog.T.API, "Fetched taxonomies ${list.taxonomyTypes.size}") + list.taxonomyTypes.forEach { type -> + appLogWrapper.d(AppLog.T.POSTS, "Taxonomy ${type.value + .name}") + } + } + + else -> { + appLogWrapper.e(AppLog.T.API, "Erro fetcing taxonomies") + } + } + } + } +} From 1a3a86447fc73c8aede3eca82356019f0a553412 Mon Sep 17 00:00:00 2001 From: adalpari Date: Thu, 2 Oct 2025 13:06:13 +0200 Subject: [PATCH 14/19] Adding show mechanism --- .../android/modules/ViewModelModule.java | 7 +++++++ .../android/ui/prefs/SiteSettingsFragment.java | 10 ++++++++++ .../taxonomies/TaxonomiesNavMenuViewModel.kt | 18 +++++++++++++----- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java index 66dc66748712..5a2f11a65562 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java @@ -49,6 +49,7 @@ import org.wordpress.android.ui.prefs.accountsettings.AccountSettingsViewModel; import org.wordpress.android.ui.prefs.homepage.HomepageSettingsViewModel; import org.wordpress.android.ui.prefs.language.LocalePickerViewModel; +import org.wordpress.android.ui.prefs.taxonomies.TaxonomiesNavMenuViewModel; import org.wordpress.android.ui.prefs.timezone.SiteSettingsTimezoneViewModel; import org.wordpress.android.ui.publicize.PublicizeListViewModel; import org.wordpress.android.ui.reader.ReaderCommentListViewModel; @@ -495,6 +496,12 @@ abstract class ViewModelModule { @ViewModelKey(BloggingRemindersViewModel.class) abstract ViewModel bloggingRemindersViewModel(BloggingRemindersViewModel viewModel); + @Binds + @IntoMap + @ViewModelKey(TaxonomiesNavMenuViewModel.class) + abstract ViewModel taxonomiesNavMenuViewModel(TaxonomiesNavMenuViewModel viewModel); + + @Binds @IntoMap @ViewModelKey(LocalePickerViewModel.class) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java index 2d5e32b26262..94dc471bd49e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java @@ -84,6 +84,7 @@ import org.wordpress.android.ui.prefs.EditTextPreferenceWithValidation.ValidationType; import org.wordpress.android.ui.prefs.SiteSettingsFormatDialog.FormatType; import org.wordpress.android.ui.prefs.homepage.HomepageSettingsDialog; +import org.wordpress.android.ui.prefs.taxonomies.TaxonomiesNavMenuViewModel; import org.wordpress.android.ui.prefs.timezone.SiteSettingsTimezoneBottomSheet; import org.wordpress.android.ui.utils.UiHelpers; import org.wordpress.android.util.AppLog; @@ -194,6 +195,8 @@ public class SiteSettingsFragment extends PreferenceFragment private BloggingRemindersViewModel mBloggingRemindersViewModel; + private TaxonomiesNavMenuViewModel mTaxonomiesNavMenuViewModel; + public SiteModel mSite; // Can interface with WP.com or WP.org @@ -1107,6 +1110,13 @@ public void initPreferences() { initBloggingSection(); removeEmptyCategories(); + initTaxonomies(); + } + + private void initTaxonomies() { + mTaxonomiesNavMenuViewModel = new ViewModelProvider(getAppCompatActivity(), mViewModelFactory) + .get(TaxonomiesNavMenuViewModel.class); + mTaxonomiesNavMenuViewModel.fetchTaxonomies(mSite); } private void updateHomepageSummary() { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt index 28ec00dcc262..24d1e4cf6c25 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt @@ -9,14 +9,18 @@ import org.wordpress.android.fluxc.utils.AppLogWrapper import org.wordpress.android.util.AppLog import rs.wordpress.api.kotlin.WpRequestResult import uniffi.wp_api.TaxonomyListParams +import uniffi.wp_api.TaxonomyType import javax.inject.Inject class TaxonomiesNavMenuViewModel @Inject constructor( private val wpApiClientProvider: WpApiClientProvider, private val appLogWrapper: AppLogWrapper, ) : ViewModel() { - fun fetchTaxonomies(site: SiteModel) { + if (!site.isUsingSelfHostedRestApi) { + appLogWrapper.d(AppLog.T.API, "Taxonomies - Taxonomies cannot be fetched: Application Password not available") + return + } viewModelScope.launch { val client = wpApiClientProvider.getWpApiClient(site) val response = client.request { requestBuilder -> @@ -25,15 +29,19 @@ class TaxonomiesNavMenuViewModel @Inject constructor( when (response) { is WpRequestResult.Success -> { val list = response.response.data - appLogWrapper.d(AppLog.T.API, "Fetched taxonomies ${list.taxonomyTypes.size}") + appLogWrapper.d(AppLog.T.API, "Taxonomies - Fetched taxonomies ${list.taxonomyTypes.size}") list.taxonomyTypes.forEach { type -> - appLogWrapper.d(AppLog.T.POSTS, "Taxonomy ${type.value - .name}") + appLogWrapper.d(AppLog.T.API, "Taxonomies - Taxonomy ${type.value.name}") + if (type.key == TaxonomyType.Category && type.value.visibility.showInNavMenus) { + appLogWrapper.d(AppLog.T.API, "Taxonomies - SHOW CATEGORIES") + } else if (type.key == TaxonomyType.PostTag && type.value.visibility.showInNavMenus) { + appLogWrapper.d(AppLog.T.API, "Taxonomies - SHOW TAGS") + } } } else -> { - appLogWrapper.e(AppLog.T.API, "Erro fetcing taxonomies") + appLogWrapper.e(AppLog.T.API, "Taxonomies - Error fetching taxonomies") } } } From 025c3ea8f08fc1295eb3a3bcc792580a9673ae2e Mon Sep 17 00:00:00 2001 From: adalpari Date: Thu, 2 Oct 2025 17:09:59 +0200 Subject: [PATCH 15/19] Showing taxoniomies --- .../ui/prefs/SiteSettingsFragment.java | 44 +++++++++++++------ .../taxonomies/TaxonomiesNavMenuViewModel.kt | 11 ++--- WordPress/src/main/res/values/key_strings.xml | 1 + WordPress/src/main/res/values/strings.xml | 2 + 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java index 94dc471bd49e..b5cf9bffe647 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java @@ -115,6 +115,8 @@ import javax.inject.Inject; import kotlin.Triple; +import kotlin.Unit; +import uniffi.wp_api.TaxonomyTypeDetailsWithEditContext; import static org.wordpress.android.ui.prefs.WPComSiteSettings.supportsJetpackSiteAcceleratorSettings; @@ -1116,7 +1118,34 @@ public void initPreferences() { private void initTaxonomies() { mTaxonomiesNavMenuViewModel = new ViewModelProvider(getAppCompatActivity(), mViewModelFactory) .get(TaxonomiesNavMenuViewModel.class); - mTaxonomiesNavMenuViewModel.fetchTaxonomies(mSite); + mTaxonomiesNavMenuViewModel.fetchTaxonomies(mSite, this::showTaxonomies); + } + + private Unit showTaxonomies(List taxonomies) { + if (taxonomies.size() < 1) { + return Unit.INSTANCE; + } + PreferenceGroup siteScreen = (PreferenceGroup) findPreference(getString(R.string.pref_key_site_screen)); + if (siteScreen != null) { + // Create taxonomies preference group + final String taxonomiesPrefKey = getString(R.string.pref_key_taxonomies); + PreferenceGroup taxonomiesPreference = (PreferenceGroup) findPreference(taxonomiesPrefKey); + if (taxonomiesPreference != null) { + WPPrefUtils.removePreference(this, R.string.pref_key_site_screen, R.string.pref_key_taxonomies); + } + taxonomiesPreference = new PreferenceCategory(getActivity()); + taxonomiesPreference.setTitle(getString(R.string.taxonomies_title)); + taxonomiesPreference.setKey(taxonomiesPrefKey); + siteScreen.addPreference(taxonomiesPreference); + + for (TaxonomyTypeDetailsWithEditContext taxonomy : taxonomies) { + Preference categoriesPref = new Preference(getActivity()); + categoriesPref.setTitle(taxonomy.getName()); + categoriesPref.setKey(taxonomy.getSlug()); + taxonomiesPreference.addPreference(categoriesPref); + } + } + return Unit.INSTANCE; } private void updateHomepageSummary() { @@ -2043,18 +2072,7 @@ private void removeNonSelfHostedPreferences() { if (group != null) { group.removeAll(); } - if (mSite.isUsingSelfHostedRestApi()) { - // Remove everything inside "Writing" preference but "Categories" and "Tags" which are now supported - WPPrefUtils.removePreference(this, R.string.pref_key_site_writing, R.string.pref_key_site_category); - WPPrefUtils.removePreference(this, R.string.pref_key_site_writing, R.string.pref_key_site_format); - WPPrefUtils.removePreference(this, R.string.pref_key_site_writing, R.string.pref_key_site_date_format); - WPPrefUtils.removePreference(this, R.string.pref_key_site_writing, R.string.pref_key_site_time_format); - WPPrefUtils.removePreference(this, R.string.pref_key_site_writing, R.string.pref_key_site_week_start); - WPPrefUtils.removePreference(this, R.string.pref_key_site_writing, R.string.pref_key_site_posts_per_page); - WPPrefUtils.removePreference(this, R.string.pref_key_site_writing, R.string.pref_key_site_related_posts); - } else { - WPPrefUtils.removePreference(this, R.string.pref_key_site_screen, R.string.pref_key_site_writing); - } + WPPrefUtils.removePreference(this, R.string.pref_key_site_screen, R.string.pref_key_site_writing); WPPrefUtils.removePreference(this, R.string.pref_key_site_screen, R.string.pref_key_site_discussion); WPPrefUtils.removePreference(this, R.string.pref_key_site_screen, R.string.pref_key_site_advanced); WPPrefUtils.removePreference(this, R.string.pref_key_site_screen, R.string.pref_key_site_quota); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt index 24d1e4cf6c25..54ac01ba4b34 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt @@ -10,13 +10,14 @@ import org.wordpress.android.util.AppLog import rs.wordpress.api.kotlin.WpRequestResult import uniffi.wp_api.TaxonomyListParams import uniffi.wp_api.TaxonomyType +import uniffi.wp_api.TaxonomyTypeDetailsWithEditContext import javax.inject.Inject class TaxonomiesNavMenuViewModel @Inject constructor( private val wpApiClientProvider: WpApiClientProvider, private val appLogWrapper: AppLogWrapper, ) : ViewModel() { - fun fetchTaxonomies(site: SiteModel) { + fun fetchTaxonomies(site: SiteModel, foo: (List) -> Unit) { if (!site.isUsingSelfHostedRestApi) { appLogWrapper.d(AppLog.T.API, "Taxonomies - Taxonomies cannot be fetched: Application Password not available") return @@ -30,14 +31,14 @@ class TaxonomiesNavMenuViewModel @Inject constructor( is WpRequestResult.Success -> { val list = response.response.data appLogWrapper.d(AppLog.T.API, "Taxonomies - Fetched taxonomies ${list.taxonomyTypes.size}") + val taxonomies = mutableListOf() list.taxonomyTypes.forEach { type -> appLogWrapper.d(AppLog.T.API, "Taxonomies - Taxonomy ${type.value.name}") - if (type.key == TaxonomyType.Category && type.value.visibility.showInNavMenus) { - appLogWrapper.d(AppLog.T.API, "Taxonomies - SHOW CATEGORIES") - } else if (type.key == TaxonomyType.PostTag && type.value.visibility.showInNavMenus) { - appLogWrapper.d(AppLog.T.API, "Taxonomies - SHOW TAGS") + if (type.value.visibility.showInNavMenus) { + taxonomies.add(type.value) } } + foo.invoke(taxonomies) } else -> { diff --git a/WordPress/src/main/res/values/key_strings.xml b/WordPress/src/main/res/values/key_strings.xml index 3c3326c8cc4e..dde04cfdb29c 100644 --- a/WordPress/src/main/res/values/key_strings.xml +++ b/WordPress/src/main/res/values/key_strings.xml @@ -17,6 +17,7 @@ wp_pref_language wp_pref_app_theme wp_pref_whats_new + wp_pref_taxonomies wp_pref_notification_blogs pref_notification_blogs_followed wp_pref_notification_other_category diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index e50bd3029a3e..2814e693a892 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -590,6 +590,8 @@ Top level Permanently delete \'%s\' Category? + Taxonomies + Add to new post From a7de2e3106112d98eeaf22ad9aa5d8e33045ec94 Mon Sep 17 00:00:00 2001 From: adalpari Date: Thu, 2 Oct 2025 17:48:01 +0200 Subject: [PATCH 16/19] Adding a LiveData --- .../android/ui/prefs/SiteSettingsFragment.java | 11 ++++++----- .../ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt | 11 ++++++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java index b5cf9bffe647..a21a1415c1ec 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java @@ -1118,7 +1118,8 @@ public void initPreferences() { private void initTaxonomies() { mTaxonomiesNavMenuViewModel = new ViewModelProvider(getAppCompatActivity(), mViewModelFactory) .get(TaxonomiesNavMenuViewModel.class); - mTaxonomiesNavMenuViewModel.fetchTaxonomies(mSite, this::showTaxonomies); + mTaxonomiesNavMenuViewModel.getTaxonomies().observe(getAppCompatActivity(), this::showTaxonomies); + mTaxonomiesNavMenuViewModel.fetchTaxonomies(mSite); } private Unit showTaxonomies(List taxonomies) { @@ -1139,10 +1140,10 @@ private Unit showTaxonomies(List taxonomies) siteScreen.addPreference(taxonomiesPreference); for (TaxonomyTypeDetailsWithEditContext taxonomy : taxonomies) { - Preference categoriesPref = new Preference(getActivity()); - categoriesPref.setTitle(taxonomy.getName()); - categoriesPref.setKey(taxonomy.getSlug()); - taxonomiesPreference.addPreference(categoriesPref); + Preference pref = new Preference(getActivity()); + pref.setTitle(taxonomy.getName()); + pref.setKey(taxonomy.getSlug()); + taxonomiesPreference.addPreference(pref); } } return Unit.INSTANCE; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt index 54ac01ba4b34..5a7b81cd86fa 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt @@ -1,5 +1,7 @@ package org.wordpress.android.ui.prefs.taxonomies +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.launch @@ -9,7 +11,6 @@ import org.wordpress.android.fluxc.utils.AppLogWrapper import org.wordpress.android.util.AppLog import rs.wordpress.api.kotlin.WpRequestResult import uniffi.wp_api.TaxonomyListParams -import uniffi.wp_api.TaxonomyType import uniffi.wp_api.TaxonomyTypeDetailsWithEditContext import javax.inject.Inject @@ -17,7 +18,11 @@ class TaxonomiesNavMenuViewModel @Inject constructor( private val wpApiClientProvider: WpApiClientProvider, private val appLogWrapper: AppLogWrapper, ) : ViewModel() { - fun fetchTaxonomies(site: SiteModel, foo: (List) -> Unit) { + // LiveData because this is observed from Java + private val _taxonomies = MutableLiveData>() + val taxonomies: LiveData> = _taxonomies + + fun fetchTaxonomies(site: SiteModel) { if (!site.isUsingSelfHostedRestApi) { appLogWrapper.d(AppLog.T.API, "Taxonomies - Taxonomies cannot be fetched: Application Password not available") return @@ -38,7 +43,7 @@ class TaxonomiesNavMenuViewModel @Inject constructor( taxonomies.add(type.value) } } - foo.invoke(taxonomies) + _taxonomies.value = taxonomies } else -> { From d37d8a2ed0fbb71fb9438ad8900a252639d61373 Mon Sep 17 00:00:00 2001 From: adalpari Date: Thu, 2 Oct 2025 17:59:28 +0200 Subject: [PATCH 17/19] Call categories and tags screens --- .../android/ui/prefs/SiteSettingsFragment.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java index a21a1415c1ec..1c37812a2f2c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java @@ -13,6 +13,7 @@ import android.os.Handler; import android.preference.EditTextPreference; import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceCategory; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; @@ -1143,6 +1144,18 @@ private Unit showTaxonomies(List taxonomies) Preference pref = new Preference(getActivity()); pref.setTitle(taxonomy.getName()); pref.setKey(taxonomy.getSlug()); + pref.setOnPreferenceClickListener(preference -> { + // TODO: Create generic taxonomies DataView and call it from here + // We are not accepting the taxonomy name as a parameter yet + // So Categories and Tags are still hardcoded + if ("category".equals(taxonomy.getSlug())) { + ActivityLauncher.showCategoriesList(getActivity(), mSite); + } else if ("post_tag".equals(taxonomy.getSlug())) { + SiteSettingsTagListActivity.showTagList(getActivity(), mSite); + } + return false; + } + ); taxonomiesPreference.addPreference(pref); } } From 6f2bcd12db0bc60033b41b44a408f85fd7e0ca28 Mon Sep 17 00:00:00 2001 From: adalpari Date: Thu, 2 Oct 2025 18:06:31 +0200 Subject: [PATCH 18/19] detekt and style --- .../wordpress/android/ui/prefs/SiteSettingsFragment.java | 9 +++------ .../ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt | 5 ++++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java index 1c37812a2f2c..4993b9669db5 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java @@ -13,7 +13,6 @@ import android.os.Handler; import android.preference.EditTextPreference; import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceCategory; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; @@ -116,7 +115,6 @@ import javax.inject.Inject; import kotlin.Triple; -import kotlin.Unit; import uniffi.wp_api.TaxonomyTypeDetailsWithEditContext; import static org.wordpress.android.ui.prefs.WPComSiteSettings.supportsJetpackSiteAcceleratorSettings; @@ -1123,9 +1121,9 @@ private void initTaxonomies() { mTaxonomiesNavMenuViewModel.fetchTaxonomies(mSite); } - private Unit showTaxonomies(List taxonomies) { - if (taxonomies.size() < 1) { - return Unit.INSTANCE; + private void showTaxonomies(List taxonomies) { + if (taxonomies.isEmpty()) { + return; } PreferenceGroup siteScreen = (PreferenceGroup) findPreference(getString(R.string.pref_key_site_screen)); if (siteScreen != null) { @@ -1159,7 +1157,6 @@ private Unit showTaxonomies(List taxonomies) taxonomiesPreference.addPreference(pref); } } - return Unit.INSTANCE; } private void updateHomepageSummary() { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt index 5a7b81cd86fa..1e01183fdac6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModel.kt @@ -24,7 +24,10 @@ class TaxonomiesNavMenuViewModel @Inject constructor( fun fetchTaxonomies(site: SiteModel) { if (!site.isUsingSelfHostedRestApi) { - appLogWrapper.d(AppLog.T.API, "Taxonomies - Taxonomies cannot be fetched: Application Password not available") + appLogWrapper.d( + AppLog.T.API, + "Taxonomies - Taxonomies cannot be fetched: Application Password not available" + ) return } viewModelScope.launch { From bbdc79f7cb3643418f37349510091d42e9d4a96a Mon Sep 17 00:00:00 2001 From: adalpari Date: Fri, 3 Oct 2025 10:47:43 +0200 Subject: [PATCH 19/19] Adding tests --- .../TaxonomiesNavMenuViewModelTest.kt | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 WordPress/src/test/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModelTest.kt diff --git a/WordPress/src/test/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModelTest.kt new file mode 100644 index 000000000000..f87417e3a6bd --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/ui/prefs/taxonomies/TaxonomiesNavMenuViewModelTest.kt @@ -0,0 +1,171 @@ +package org.wordpress.android.ui.prefs.taxonomies + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.wordpress.android.BaseUnitTest +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider +import org.wordpress.android.fluxc.utils.AppLogWrapper +import org.wordpress.android.util.AppLog +import rs.wordpress.api.kotlin.WpApiClient +import rs.wordpress.api.kotlin.WpRequestResult +import uniffi.wp_api.TaxonomiesRequestListWithEditContextResponse +import uniffi.wp_api.TaxonomyType +import uniffi.wp_api.TaxonomyTypeDetailsWithEditContext +import uniffi.wp_api.TaxonomyTypesResponseWithEditContext +import uniffi.wp_api.TaxonomyTypeVisibility +import uniffi.wp_api.WpNetworkHeaderMap + +@ExperimentalCoroutinesApi +class TaxonomiesNavMenuViewModelTest : BaseUnitTest() { + @Mock + private lateinit var wpApiClientProvider: WpApiClientProvider + + @Mock + private lateinit var wpApiClient: WpApiClient + + @Mock + private lateinit var appLogWrapper: AppLogWrapper + + private lateinit var viewModel: TaxonomiesNavMenuViewModel + + private var taxonomies: List = listOf() + + private val testSite = SiteModel().apply { + id = 123 + url = "https://test.wordpress.com" + apiRestUsernamePlain = "user" + apiRestPasswordPlain = "pass" + setIsWPCom(false) + } + + @Before + fun setUp() { + MockitoAnnotations.openMocks(this) + + whenever(wpApiClientProvider.getWpApiClient(testSite)).thenReturn(wpApiClient) + + viewModel = TaxonomiesNavMenuViewModel( + wpApiClientProvider, + appLogWrapper + ) + viewModel.taxonomies.observeForever { taxonomies = it } + } + + @Test + fun `when site does not support self-hosted rest api, then taxonomies are not fetched`() = test { + testSite.setIsWPCom(true) + + viewModel.fetchTaxonomies(testSite) + advanceUntilIdle() + + verify(wpApiClientProvider, never()).getWpApiClient(any(), any()) + verify(appLogWrapper).d( + AppLog.T.API, + "Taxonomies - Taxonomies cannot be fetched: Application Password not available" + ) + assertTrue(taxonomies.isEmpty()) + } + + @Test + fun `when LiveData is observed, it starts with null value`() { + assertNotNull(viewModel.taxonomies) + assertEquals(null, viewModel.taxonomies.value) + } + + @Test + fun `fetch taxonomies with success response dispatches success action`() = runTest { + // Create the correct response structure following the MediaRSApiRestClientTest pattern + val response = TaxonomiesRequestListWithEditContextResponse( + createTestTaxonomyTypesResponseWithEditContext(), + mock(), + ) + + val successResponse: WpRequestResult = WpRequestResult.Success( + response = response + ) + + whenever(wpApiClient.request(any())).thenReturn(successResponse) + + viewModel.fetchTaxonomies(testSite) + advanceUntilIdle() + + val responseList: List = response.data.taxonomyTypes.map { it.value } + assertEquals(responseList, taxonomies) + } + + @Test + fun `fetch taxonomies with error response do nothing`() = runTest { + // Use a concrete error type that we can create - UnknownError requires statusCode and response + val errorResponse = WpRequestResult.UnknownError( + statusCode = 500u, + response = "Internal Server Error" + ) + + whenever(wpApiClient.request(any())).thenReturn(errorResponse) + + viewModel.fetchTaxonomies(testSite) + + verify(appLogWrapper).e(any(), any()) + assertTrue(taxonomies.isEmpty()) + } + + private fun createTestTaxonomyTypesResponseWithEditContext(): TaxonomyTypesResponseWithEditContext { + val visibility = TaxonomyTypeVisibility( + public = true, + publiclyQueryable = true, + showUi = true, + showAdminColumn = true, + showInNavMenus = true, + showInQuickEdit = true + ) + + val categoryDetails = TaxonomyTypeDetailsWithEditContext( + name = "Categories", + slug = "category", + description = "Test categories", + visibility = visibility, + restBase = "categories", + restNamespace = "wp/v2", + types = listOf("post"), + hierarchical = true, + showCloud = true, + capabilities = mock(), + labels = mock() + ) + + val tagDetails = TaxonomyTypeDetailsWithEditContext( + name = "Tags", + slug = "post_tag", + description = "Test tags", + visibility = visibility, + restBase = "tags", + restNamespace = "wp/v2", + types = listOf("post"), + hierarchical = false, + showCloud = true, + capabilities = mock(), + labels = mock() + ) + + return TaxonomyTypesResponseWithEditContext( + mapOf( + TaxonomyType.Category to categoryDetails, + TaxonomyType.PostTag to tagDetails + ) + ) + } +}