From f3ff9689a0a998537a624ce9a77b930a341bee48 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Fri, 10 May 2024 13:58:14 +0200 Subject: [PATCH 1/2] Add a way to fetch orders using coroutines without deleting existing data --- .../rest/wpcom/wc/order/OrderRestClient.kt | 91 ++++++++++++++++--- .../android/fluxc/store/WCOrderStore.kt | 32 ++++++- 2 files changed, 109 insertions(+), 14 deletions(-) diff --git a/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/order/OrderRestClient.kt b/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/order/OrderRestClient.kt index 1d56fb16a2..77eb742906 100644 --- a/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/order/OrderRestClient.kt +++ b/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/order/OrderRestClient.kt @@ -75,16 +75,13 @@ class OrderRestClient @Inject constructor( */ fun fetchOrders(site: SiteModel, offset: Int, filterByStatus: String? = null) { coroutineEngine.launch(T.API, this, "fetchOrders") { - // If null, set the filter to the api default value of "any", which will not apply any order status filters. - val statusFilter = filterByStatus.takeUnless { it.isNullOrBlank() } - ?: WCOrderStore.DEFAULT_ORDER_STATUS - val url = WOOCOMMERCE.orders.pathV3 - val params = mapOf( + val params = mutableMapOf( "per_page" to WCOrderStore.NUM_ORDERS_PER_FETCH.toString(), "offset" to offset.toString(), - "status" to statusFilter, "_fields" to ORDER_FIELDS + ).putIfNotEmpty( + "status" to filterByStatus ) val response = wooNetwork.executeGetGsonRequest( @@ -115,6 +112,66 @@ class OrderRestClient @Inject constructor( } } + /** + * Fetches orders from the API. + * + * Makes a GET call to `/wp-json/wc/v3/orders` retrieving a list of orders for the given site and parameters. + * + * @param site The WooCommerce [SiteModel] the orders belong to + * @param count The number of orders to fetch + * @param page The page number to fetch + * @param orderBy The field to order the results by + * @param sortOrder The order to sort the results by + * @param statusFilter The status to filter the results by + */ + @Suppress("LongParameterList") + suspend fun fetchOrders( + site: SiteModel, + count: Int, + page: Int, + orderBy: OrderBy, + sortOrder: SortOrder, + statusFilter: String? + ): FetchOrdersResponsePayload { + val url = WOOCOMMERCE.orders.pathV3 + val params = mutableMapOf( + "per_page" to count.toString(), + "page" to page.toString(), + "orderby" to orderBy.value, + "order" to sortOrder.value, + "_fields" to ORDER_FIELDS + ).putIfNotEmpty( + "status" to statusFilter + ) + + val response = wooNetwork.executeGetGsonRequest( + site = site, + path = url, + params = params, + clazz = Array::class.java + ) + + when (response) { + is WPAPIResponse.Success -> { + val orderModels = response.data?.map { orderDto -> + orderDtoMapper.toDatabaseEntity(orderDto, site.localId()) + }.orEmpty() + + val canLoadMore = orderModels.size == WCOrderStore.NUM_ORDERS_PER_FETCH + return FetchOrdersResponsePayload( + site = site, + ordersWithMeta = orderModels, + loadedMore = page > 1, + canLoadMore = canLoadMore + ) + } + is WPAPIResponse.Error -> { + val orderError = wpAPINetworkErrorToOrderError(response.error) + return FetchOrdersResponsePayload(orderError, site) + } + } + } + /** * Fetches orders from the API, but only requests `id` and `date_created_gmt` fields be returned. This is * used to determine what orders should be fetched (either existing orders that have since changed or new @@ -135,16 +192,11 @@ class OrderRestClient @Inject constructor( requestStartTime: Calendar ) { coroutineEngine.launch(T.API, this, "fetchOrderListSummaries") { - // If null, set the filter to the api default value of "any", which will not apply any order status filters. - val statusFilter = listDescriptor.statusFilter.takeUnless { it.isNullOrBlank() } - ?: WCOrderStore.DEFAULT_ORDER_STATUS - val url = WOOCOMMERCE.orders.pathV3 val networkPageSize = listDescriptor.config.networkPageSize val params = mutableMapOf( "per_page" to networkPageSize.toString(), "offset" to offset.toString(), - "status" to statusFilter, "_fields" to "id,date_created_gmt,date_modified_gmt" ).putIfNotEmpty( "search" to listDescriptor.searchQuery, @@ -152,7 +204,8 @@ class OrderRestClient @Inject constructor( "after" to listDescriptor.afterFilter, "customer" to listDescriptor.customerId?.toString(), "product" to listDescriptor.productId?.toString(), - "exclude" to listDescriptor.excludedIds?.joinToString() + "exclude" to listDescriptor.excludedIds?.joinToString(), + "status" to listDescriptor.statusFilter.takeUnless { it.isNullOrBlank() } ) val response = wooNetwork.executeGetGsonRequest( @@ -299,7 +352,6 @@ class OrderRestClient @Inject constructor( val params = mutableMapOf( "per_page" to WCOrderStore.NUM_ORDERS_PER_FETCH.toString(), "offset" to offset.toString(), - "status" to WCOrderStore.DEFAULT_ORDER_STATUS, "_fields" to ORDER_FIELDS ).putIfNotEmpty("search" to searchQuery) @@ -1051,4 +1103,17 @@ class OrderRestClient @Inject constructor( "tracking_provider" ).joinToString(separator = ",") } + + enum class SortOrder(val value: String) { + ASCENDING("asc"), + DESCENDING("desc"); + } + + enum class OrderBy(val value: String) { + DATE("date"), + ID("id"), + INCLUDE("include"), + TITLE("title"), + SLUG("slug"); + } } diff --git a/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/store/WCOrderStore.kt b/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/store/WCOrderStore.kt index f16233046c..df39b6dc97 100644 --- a/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/store/WCOrderStore.kt +++ b/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/store/WCOrderStore.kt @@ -21,8 +21,13 @@ import org.wordpress.android.fluxc.model.WCOrderShipmentTrackingModel import org.wordpress.android.fluxc.model.WCOrderStatusModel import org.wordpress.android.fluxc.model.WCOrderSummaryModel import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError +import org.wordpress.android.fluxc.network.BaseRequest.GenericErrorType.SERVER_ERROR +import org.wordpress.android.fluxc.network.rest.wpcom.wc.WooError +import org.wordpress.android.fluxc.network.rest.wpcom.wc.WooErrorType.API_ERROR import org.wordpress.android.fluxc.network.rest.wpcom.wc.WooResult import org.wordpress.android.fluxc.network.rest.wpcom.wc.order.OrderRestClient +import org.wordpress.android.fluxc.network.rest.wpcom.wc.order.OrderRestClient.OrderBy +import org.wordpress.android.fluxc.network.rest.wpcom.wc.order.OrderRestClient.SortOrder import org.wordpress.android.fluxc.persistence.OrderSqlUtils import org.wordpress.android.fluxc.persistence.dao.OrderMetaDataDao import org.wordpress.android.fluxc.persistence.dao.OrderNotesDao @@ -59,7 +64,6 @@ class WCOrderStore @Inject constructor( ) : Store(dispatcher) { companion object { const val NUM_ORDERS_PER_FETCH = 15 - const val DEFAULT_ORDER_STATUS = "any" } class FetchOrdersPayload( @@ -598,6 +602,32 @@ class WCOrderStore @Inject constructor( } } + @Suppress("SpreadOperator") + suspend fun fetchOrders( + site: SiteModel, + count: Int = NUM_ORDERS_PER_FETCH, + page: Int = 1, + orderBy: OrderBy = OrderBy.DATE, + sortOrder: SortOrder = SortOrder.DESCENDING, + statusFilter: String? = null, + deleteOldData: Boolean = false + ): WooResult> { + return coroutineEngine.withDefaultContext(API, this, "fetchOrders") { + val result = wcOrderRestClient.fetchOrders(site, count, page, orderBy, sortOrder, statusFilter) + + return@withDefaultContext if (result.isError) { + WooResult(WooError(API_ERROR, SERVER_ERROR, result.error.message)) + } else { + if (deleteOldData) { + ordersDaoDecorator.deleteOrdersForSite(site.localId()) + } + insertOrder(site.localId(), *result.ordersWithMeta.toTypedArray()) + + WooResult(result.orders) + } + } + } + suspend fun updateOrderStatus( orderId: Long, site: SiteModel, From e4de4f09f9ca3c55306362d13a6d3f1adf7c9902 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Fri, 10 May 2024 16:48:27 +0200 Subject: [PATCH 2/2] Delete orders by default if the 1st page is requested --- .../kotlin/org/wordpress/android/fluxc/store/WCOrderStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/store/WCOrderStore.kt b/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/store/WCOrderStore.kt index df39b6dc97..f4041e5783 100644 --- a/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/store/WCOrderStore.kt +++ b/plugins/woocommerce/src/main/kotlin/org/wordpress/android/fluxc/store/WCOrderStore.kt @@ -610,7 +610,7 @@ class WCOrderStore @Inject constructor( orderBy: OrderBy = OrderBy.DATE, sortOrder: SortOrder = SortOrder.DESCENDING, statusFilter: String? = null, - deleteOldData: Boolean = false + deleteOldData: Boolean = page == 1 ): WooResult> { return coroutineEngine.withDefaultContext(API, this, "fetchOrders") { val result = wcOrderRestClient.fetchOrders(site, count, page, orderBy, sortOrder, statusFilter)