diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/FetchAccountSettings.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/FetchAccountSettings.kt new file mode 100644 index 00000000000..0e18439a7f3 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/FetchAccountSettings.kt @@ -0,0 +1,28 @@ +package com.woocommerce.android.ui.orders.wooshippinglabels + +import com.woocommerce.android.tools.SelectedSite +import com.woocommerce.android.ui.orders.wooshippinglabels.models.StoreOptionsModel +import com.woocommerce.android.ui.orders.wooshippinglabels.networking.WooShippingLabelRepository +import javax.inject.Inject + +class FetchAccountSettings @Inject constructor( + private val shippingRepository: WooShippingLabelRepository, + private val selectedSite: SelectedSite +) { + suspend operator fun invoke(): Result { + return selectedSite.getOrNull()?.let { + val response = shippingRepository.fetchAccountSettings(it) + val result = response.model + when { + response.isError.not() && result != null && result != StoreOptionsModel.EMPTY -> { + Result.success(result) + } + + else -> { + val message = response.error?.message ?: "Unknown error" + Result.failure(Exception(message)) + } + } + } ?: Result.failure(Exception("No site selected")) + } +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt index 52de38057d8..cecdc675218 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt @@ -46,15 +46,10 @@ class WooShippingLabelCreationViewModel @Inject constructor( private val getShippableItems: GetShippableItems, private val currencyFormatter: CurrencyFormatter, private val observeOriginAddresses: ObserveOriginAddresses, - private val getShippingRates: GetShippingRates + private val getShippingRates: GetShippingRates, + private val fetchAccountSettings: FetchAccountSettings ) : ScopedViewModel(savedState) { private val navArgs: WooShippingLabelCreationFragmentArgs by savedState.navArgs() - private val mockStoreOptions = StoreOptionsModel( - currencySymbol = "$", - dimensionUnit = "cm", - weightUnit = "kg", - originCountry = "US" - ) private val emptyOrder = Order.getEmptyOrder(Date(), Date()) private val order = MutableStateFlow(emptyOrder) @@ -103,7 +98,16 @@ class WooShippingLabelCreationViewModel @Inject constructor( } private fun getStoreOptions() { - storeOptions.value = mockStoreOptions + launch { + fetchAccountSettings().fold( + onSuccess = { + storeOptions.value = it + }, + onFailure = { + storeOptions.value = null + } + ) + } } @Suppress("ComplexCondition") @@ -179,14 +183,18 @@ class WooShippingLabelCreationViewModel @Inject constructor( } private suspend fun observePackageChanges() { - packageSelected.combine(packageWeight) { packageSelected, packageWeight -> + combine( + packageSelected, + packageWeight, + storeOptions + ) { packageSelected, packageWeight, storeOptions -> if (packageSelected == null || packageWeight == null) { NotSelected } else { DataAvailable( selectedPackage = packageSelected, defaultWeight = packageWeight.defaultWeight.toString(), - weightUnit = mockStoreOptions.weightUnit + weightUnit = storeOptions?.weightUnit ?: "" ) } }.collectLatest { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/DTOs.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/DTOs.kt new file mode 100644 index 00000000000..b5e53fe2214 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/DTOs.kt @@ -0,0 +1,14 @@ +package com.woocommerce.android.ui.orders.wooshippinglabels.networking + +import com.google.gson.annotations.SerializedName + +data class AccountSettingsDTO( + val storeOptions: StoreOptionsDTO +) + +data class StoreOptionsDTO( + @SerializedName("currency_symbol") val currencySymbol: String? = null, + @SerializedName("dimension_unit") val dimensionUnit: String? = null, + @SerializedName("weight_unit") val weightUnit: String? = null, + @SerializedName("origin_country") val originCountry: String? = null +) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingLabelRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingLabelRepository.kt index 7f693c7e815..4ef545bcb13 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingLabelRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingLabelRepository.kt @@ -4,7 +4,8 @@ import org.wordpress.android.fluxc.model.SiteModel import javax.inject.Inject class WooShippingLabelRepository @Inject constructor( - private val restClient: WooShippingLabelRestClient + private val restClient: WooShippingLabelRestClient, + private val mapper: WooShippingNetworkingMapper ) { suspend fun fetchShippingLabelPrinting( site: SiteModel, @@ -15,4 +16,10 @@ class WooShippingLabelRepository @Inject constructor( labelIds = labelIds, paperSize = paperSize ).asWooResult() + + suspend fun fetchAccountSettings( + site: SiteModel, + ) = restClient.fetchAccountSettings( + site = site, + ).asWooResult { mapper(it.storeOptions) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingLabelRestClient.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingLabelRestClient.kt index 0dc02508f9c..a407872cc49 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingLabelRestClient.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingLabelRestClient.kt @@ -27,4 +27,18 @@ class WooShippingLabelRestClient @Inject constructor( clazz = ShippingLabelPrintingResponse::class.java, ).toWooPayload() } + + suspend fun fetchAccountSettings( + site: SiteModel, + ): WooPayload { + val url = "/wcshipping/v1/account/settings/" + + val result = wooNetwork.executeGetGsonRequest( + site = site, + path = url, + clazz = AccountSettingsDTO::class.java, + ) + + return result.toWooPayload() + } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingNetworkingMapper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingNetworkingMapper.kt new file mode 100644 index 00000000000..7e9ffc1f206 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingNetworkingMapper.kt @@ -0,0 +1,15 @@ +package com.woocommerce.android.ui.orders.wooshippinglabels.networking + +import com.woocommerce.android.ui.orders.wooshippinglabels.models.StoreOptionsModel +import javax.inject.Inject + +class WooShippingNetworkingMapper @Inject constructor() { + operator fun invoke(storeOptionsDTO: StoreOptionsDTO): StoreOptionsModel { + return StoreOptionsModel( + currencySymbol = storeOptionsDTO.currencySymbol.orEmpty(), + dimensionUnit = storeOptionsDTO.dimensionUnit.orEmpty(), + weightUnit = storeOptionsDTO.weightUnit.orEmpty(), + originCountry = storeOptionsDTO.originCountry.orEmpty() + ) + } +} diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/FetchAccountSettingsTests.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/FetchAccountSettingsTests.kt new file mode 100644 index 00000000000..112517a9e20 --- /dev/null +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/FetchAccountSettingsTests.kt @@ -0,0 +1,76 @@ +package com.woocommerce.android.ui.orders.wooshippinglabels + +import com.woocommerce.android.tools.SelectedSite +import com.woocommerce.android.ui.orders.wooshippinglabels.models.StoreOptionsModel +import com.woocommerce.android.ui.orders.wooshippinglabels.networking.WooShippingLabelRepository +import com.woocommerce.android.viewmodel.BaseUnitTest +import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.network.BaseRequest +import org.wordpress.android.fluxc.network.rest.wpcom.wc.WooError +import org.wordpress.android.fluxc.network.rest.wpcom.wc.WooErrorType +import org.wordpress.android.fluxc.network.rest.wpcom.wc.WooResult +import kotlin.test.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class FetchAccountSettingsTests : BaseUnitTest() { + private val shippingRepository: WooShippingLabelRepository = mock() + private val selectedSite: SelectedSite = mock { + on { getOrNull() } doReturn SiteModel().apply { + url = "https://example.com" + } + } + + private val defaultStoreOptions = StoreOptionsModel( + weightUnit = "kg", + currencySymbol = "$", + dimensionUnit = "cm", + originCountry = "US" + ) + + val sut = FetchAccountSettings( + shippingRepository = shippingRepository, + selectedSite = selectedSite + ) + + @Test + fun `when selected site is null then return failure`() = testBlocking { + whenever(selectedSite.getOrNull()).doReturn(null) + val result = sut.invoke() + assert(result.isFailure) + } + + @Test + fun `when fetch account settings fails then return failure`() = testBlocking { + whenever(shippingRepository.fetchAccountSettings(any())).doReturn( + WooResult(WooError(WooErrorType.GENERIC_ERROR, BaseRequest.GenericErrorType.UNKNOWN)) + ) + + val result = sut.invoke() + assert(result.isFailure) + } + + @Test + fun `when fetch account settings is empty then return failure`() = testBlocking { + whenever(shippingRepository.fetchAccountSettings(any())).doReturn( + WooResult(StoreOptionsModel.EMPTY) + ) + + val result = sut.invoke() + assert(result.isFailure) + } + + @Test + fun `when fetch account settings succeed then return success`() = testBlocking { + whenever(shippingRepository.fetchAccountSettings(any())).doReturn( + WooResult(defaultStoreOptions) + ) + + val result = sut.invoke() + assert(result.isSuccess) + } +} diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModelTest.kt index 885a8ab5959..59b02a9e1e4 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModelTest.kt @@ -11,6 +11,7 @@ import com.woocommerce.android.ui.orders.wooshippinglabels.WooShippingLabelCreat import com.woocommerce.android.ui.orders.wooshippinglabels.WooShippingLabelCreationViewModel.WooShippingViewState.DataState import com.woocommerce.android.ui.orders.wooshippinglabels.models.OriginShippingAddress import com.woocommerce.android.ui.orders.wooshippinglabels.models.ShippableItemModel +import com.woocommerce.android.ui.orders.wooshippinglabels.models.StoreOptionsModel import com.woocommerce.android.ui.orders.wooshippinglabels.models.WooShippingCarrier import com.woocommerce.android.ui.orders.wooshippinglabels.packages.ui.PackageData import com.woocommerce.android.ui.orders.wooshippinglabels.rates.datasource.WooShippingRateModel @@ -86,6 +87,12 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { private val defaultShipToAddress = Address.EMPTY.copy( address1 = "1278 24st Perito AVE" ) + private val defaultStoreOptions = StoreOptionsModel( + weightUnit = "kg", + currencySymbol = "$", + dimensionUnit = "cm", + originCountry = "US" + ) private val defaultPackageData = PackageData( id = "1", @@ -152,6 +159,7 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { private val observeOriginAddresses: ObserveOriginAddresses = mock() private val getShippingRates: GetShippingRates = mock() + private val fetchAccountSettings: FetchAccountSettings = mock() private lateinit var sut: WooShippingLabelCreationViewModel @@ -162,6 +170,7 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { currencyFormatter = currencyFormatter, observeOriginAddresses = observeOriginAddresses, getShippingRates = getShippingRates, + fetchAccountSettings = fetchAccountSettings, savedState = savedState ) } @@ -178,6 +187,7 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { whenever(orderDetailRepository.getOrderById(any())) doReturn order whenever(getShippableItems(any())) doReturn defaultShippableItems whenever(observeOriginAddresses()) doReturn flowOf(defaultOriginAddresses) + whenever(fetchAccountSettings()) doReturn Result.success(defaultStoreOptions) createViewModel() @@ -201,6 +211,7 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { whenever(orderDetailRepository.getOrderById(any())) doReturn order whenever(getShippableItems(any())) doReturn defaultShippableItems whenever(observeOriginAddresses()) doReturn flowOf(defaultOriginAddresses) + whenever(fetchAccountSettings()) doReturn Result.success(defaultStoreOptions) createViewModel() @@ -251,6 +262,7 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { whenever(orderDetailRepository.getOrderById(any())) doReturn order whenever(getShippableItems(any())) doReturn defaultShippableItems whenever(observeOriginAddresses()) doReturn flowOf(defaultOriginAddresses) + whenever(fetchAccountSettings()) doReturn Result.success(defaultStoreOptions) createViewModel() @@ -279,6 +291,7 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { whenever( getShippingRates(any(), any(), any(), any(), any(), any()) ) doReturn Result.success(defaultShippingRates) + whenever(fetchAccountSettings()) doReturn Result.success(defaultStoreOptions) createViewModel() @@ -307,6 +320,7 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { whenever( getShippingRates(any(), any(), any(), any(), any(), any()) ) doReturn Result.failure(Exception("Random error")) + whenever(fetchAccountSettings()) doReturn Result.success(defaultStoreOptions) createViewModel() sut.onPackageSelected(defaultPackageData) @@ -329,7 +343,6 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { ) ) whenever(orderDetailRepository.getOrderById(any())) doReturn order - whenever(getShippableItems(any())) doReturn defaultShippableItems whenever(observeOriginAddresses()) doReturn flowOf(defaultOriginAddresses) whenever( getShippingRates(any(), any(), any(), any(), any(), any()) @@ -358,7 +371,6 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { ) ) whenever(orderDetailRepository.getOrderById(any())) doReturn order - whenever(getShippableItems(any())) doReturn defaultShippableItems whenever(observeOriginAddresses()) doReturn flowOf(defaultOriginAddresses) whenever( getShippingRates(any(), any(), any(), any(), any(), any()) @@ -387,7 +399,6 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { ) ) whenever(orderDetailRepository.getOrderById(any())) doReturn order - whenever(getShippableItems(any())) doReturn defaultShippableItems whenever(observeOriginAddresses()) doReturn flowOf(defaultOriginAddresses) whenever( getShippingRates(any(), any(), any(), any(), any(), any()) @@ -415,6 +426,7 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { whenever(orderDetailRepository.getOrderById(any())) doReturn order whenever(getShippableItems(any())) doReturn defaultShippableItems whenever(observeOriginAddresses()) doReturn flowOf(defaultOriginAddresses) + whenever(fetchAccountSettings()) doReturn Result.success(defaultStoreOptions) createViewModel() @@ -454,6 +466,7 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() { whenever(orderDetailRepository.getOrderById(any())) doReturn order whenever(getShippableItems(any())) doReturn defaultShippableItems whenever(observeOriginAddresses()) doReturn flowOf(defaultOriginAddresses) + whenever(fetchAccountSettings()) doReturn Result.success(defaultStoreOptions) createViewModel() diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingNetworkingMapperTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingNetworkingMapperTest.kt new file mode 100644 index 00000000000..95ee639c602 --- /dev/null +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/networking/WooShippingNetworkingMapperTest.kt @@ -0,0 +1,43 @@ +package com.woocommerce.android.ui.orders.wooshippinglabels.networking + +import com.woocommerce.android.ui.orders.wooshippinglabels.models.StoreOptionsModel +import com.woocommerce.android.viewmodel.BaseUnitTest +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlin.test.Test +import kotlin.test.assertEquals + +@OptIn(ExperimentalCoroutinesApi::class) +class WooShippingNetworkingMapperTest : BaseUnitTest() { + val sut = WooShippingNetworkingMapper() + + @Test + fun `when all field are null then return empty model`() { + val emptyDTO = StoreOptionsDTO( + weightUnit = null, + currencySymbol = null, + dimensionUnit = null, + originCountry = null + ) + + val result = sut.invoke(emptyDTO) + + assert(result == StoreOptionsModel.EMPTY) + } + + @Test + fun `when a dto is received then return the expected model`() { + val dto = StoreOptionsDTO( + weightUnit = "kg", + currencySymbol = "$", + dimensionUnit = "cm", + originCountry = "US" + ) + + val result = sut.invoke(dto) + + assertEquals(result.currencySymbol, dto.currencySymbol) + assertEquals(result.weightUnit, dto.weightUnit) + assertEquals(result.dimensionUnit, dto.dimensionUnit) + assertEquals(result.originCountry, dto.originCountry) + } +}