Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ECWID-153684 - added new method searchBrands in API Client. #446

Merged
merged 9 commits into from
Dec 17, 2024
Merged
5 changes: 3 additions & 2 deletions src/main/kotlin/com/ecwid/apiclient/v3/ApiClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ import com.ecwid.apiclient.v3.dto.customergroup.request.*
import com.ecwid.apiclient.v3.dto.customergroup.result.*
import com.ecwid.apiclient.v3.dto.instantsite.redirects.request.*
import com.ecwid.apiclient.v3.dto.instantsite.redirects.result.*
import com.ecwid.apiclient.v3.dto.order.request.*
import com.ecwid.apiclient.v3.dto.order.result.*
import com.ecwid.apiclient.v3.dto.productreview.request.*
import com.ecwid.apiclient.v3.dto.productreview.result.*
import com.ecwid.apiclient.v3.dto.producttype.request.*
Expand Down Expand Up @@ -55,6 +53,7 @@ import kotlin.reflect.KClass
open class ApiClient private constructor(
protected val apiClientHelper: ApiClientHelper,
storeProfileApiClient: StoreProfileApiClient,
brandsApiClient: BrandsApiClient,
productsApiClient: ProductsApiClient,
categoriesApiClient: CategoriesApiClient,
ordersApiClient: OrdersApiClient,
Expand All @@ -76,6 +75,7 @@ open class ApiClient private constructor(
storeExtrafieldsApiClient: StoreExtrafieldsApiClientImpl,
) :
StoreProfileApiClient by storeProfileApiClient,
BrandsApiClient by brandsApiClient,
ProductsApiClient by productsApiClient,
CategoriesApiClient by categoriesApiClient,
OrdersApiClient by ordersApiClient,
Expand All @@ -99,6 +99,7 @@ open class ApiClient private constructor(
constructor(apiClientHelper: ApiClientHelper) : this(
apiClientHelper = apiClientHelper,
storeProfileApiClient = StoreProfileApiClientImpl(apiClientHelper),
brandsApiClient = BrandsApiClientImpl(apiClientHelper),
productsApiClient = ProductsApiClientImpl(apiClientHelper),
categoriesApiClient = CategoriesApiClientImpl(apiClientHelper),
ordersApiClient = OrdersApiClientImpl(apiClientHelper),
Expand Down
21 changes: 21 additions & 0 deletions src/main/kotlin/com/ecwid/apiclient/v3/BrandsApiClient.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.ecwid.apiclient.v3

import com.ecwid.apiclient.v3.dto.brand.request.BrandsSearchRequest
import com.ecwid.apiclient.v3.dto.brand.result.BrandsSearchResult
import com.ecwid.apiclient.v3.dto.common.PartialResult
import kotlin.reflect.KClass

// Brands
// https://api-docs.ecwid.com/reference/product-brands
interface BrandsApiClient {
fun searchBrands(request: BrandsSearchRequest): BrandsSearchResult
fun <Result> searchBrands(request: BrandsSearchRequest, resultClass: KClass<Result>): Result
where Result : PartialResult<BrandsSearchResult>
}

@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
inline fun <reified Result: PartialResult<BrandsSearchResult>> BrandsApiClient.searchBrands(
Fixed Show fixed Hide fixed
request: BrandsSearchRequest
): Result {
return searchBrands(request, resultClass = Result::class)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.ecwid.apiclient.v3.dto.brand.request

import com.ecwid.apiclient.v3.dto.ApiRequest
import com.ecwid.apiclient.v3.dto.common.PagingRequest
import com.ecwid.apiclient.v3.impl.RequestInfo
import com.ecwid.apiclient.v3.responsefields.ResponseFields

sealed class BrandsSearchRequest : ApiRequest {

data class ByFilters(
val limit: Int = 100,
override val offset: Int = 0,
val lang: String? = null,
val hiddenBrands: Boolean? = null,
val baseUrl: String? = null,
val cleanUrls: Boolean? = null,
val sortBy: SortOrder? = null,
val responseFields: ResponseFields = ResponseFields.All,
) : BrandsSearchRequest(), PagingRequest<ByFilters> {
override fun toRequestInfo() = RequestInfo.createGetRequest(
pathSegments = listOf(
"brands",
),
params = toParams(),
responseFields = responseFields,
)

private fun toParams(): Map<String, String> {
val request = this
return mutableMapOf<String, String>().apply {
put("limit", request.limit.toString())
put("offset", request.offset.toString())
request.lang?.let { put("lang", it) }
request.hiddenBrands?.let { put("hiddenBrands", it.toString()) }
request.baseUrl?.let { put("baseUrl", it) }
request.cleanUrls?.let { put("cleanUrls", it.toString()) }
request.sortBy?.let { put("sortBy", it.name) }
}.toMap()
}

override fun copyWithOffset(offset: Int) = copy(offset = offset)
}

@Suppress("unused")
enum class SortOrder {
PRODUCT_COUNT_DESC,
NAME_ASC,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ecwid.apiclient.v3.dto.brand.result

import com.ecwid.apiclient.v3.dto.common.ApiResultDTO

data class BrandsSearchResult(
val items: List<FetchedBrand> = listOf(),
val count: Int = 0,
val total: Int = 0,
val limit: Int = 0,
val offset: Int = 0
) : ApiResultDTO
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.ecwid.apiclient.v3.dto.brand.result

import com.ecwid.apiclient.v3.dto.common.ApiFetchedDTO
import com.ecwid.apiclient.v3.dto.common.ApiResultDTO

data class FetchedBrand(
val name: String = "",
val nameTranslated: Map<String, String>? = null,
val productsFilteredByBrandUrl: String? = null,
) : ApiFetchedDTO, ApiResultDTO {
override fun getModifyKind() = ApiFetchedDTO.ModifyKind.ReadOnly
}
26 changes: 26 additions & 0 deletions src/main/kotlin/com/ecwid/apiclient/v3/impl/BrandsApiClientImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.ecwid.apiclient.v3.impl

import com.ecwid.apiclient.v3.ApiClientHelper
import com.ecwid.apiclient.v3.BrandsApiClient
import com.ecwid.apiclient.v3.dto.brand.request.BrandsSearchRequest
import com.ecwid.apiclient.v3.dto.brand.result.BrandsSearchResult
import com.ecwid.apiclient.v3.dto.common.PartialResult
import kotlin.reflect.KClass

internal class BrandsApiClientImpl(
private val apiClientHelper: ApiClientHelper,
) : BrandsApiClient {

override fun searchBrands(request: BrandsSearchRequest) =
apiClientHelper.makeObjectResultRequest<BrandsSearchResult>(request)

override fun <Result : PartialResult<BrandsSearchResult>> searchBrands(
request: BrandsSearchRequest,
resultClass: KClass<Result>
): Result {
return apiClientHelper.makeObjectPartialResultRequest(
request = request,
resultClass = resultClass,
)
}
}
96 changes: 96 additions & 0 deletions src/test/kotlin/com/ecwid/apiclient/v3/entity/BrandsTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.ecwid.apiclient.v3.entity

import com.ecwid.apiclient.v3.dto.brand.request.BrandsSearchRequest
import com.ecwid.apiclient.v3.dto.product.request.ProductCreateRequest
import com.ecwid.apiclient.v3.dto.product.request.ProductsSearchRequest.ByFilters
import com.ecwid.apiclient.v3.dto.product.request.UpdatedProduct
import com.ecwid.apiclient.v3.dto.product.request.UpdatedProduct.*
import com.ecwid.apiclient.v3.util.randomAlphanumeric
import com.ecwid.apiclient.v3.util.randomPrice
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import kotlin.test.Test
import kotlin.test.assertEquals

class BrandsTest : BaseEntityTest() {

@BeforeEach
override fun beforeEach() {
super.beforeEach()

initStoreProfile()
removeAllProducts()
}

@Test
fun getBrands() {
val brandedProductsCreateResult = createProductsWithBrands()

// Waiting till product became available for searching
brandedProductsCreateResult.skus.forEach { sku ->
waitForIndexedProducts(
productsSearchRequest = ByFilters(sku = sku),
desiredProductCount = 1
)
}

val result = apiClient.searchBrands(BrandsSearchRequest.ByFilters())
assertEquals(
brandedProductsCreateResult.brandNames,
result.items.map { it.name }
)
}

private fun createProductsWithBrands(productsCount: Int = 5): BrandedProductsCreateResult {
val brands = mutableListOf<String>()
val skus = mutableListOf<String>()

for (i in 1..productsCount) {
Fixed Show fixed Hide fixed
val randomBrand = randomAlphanumeric(8)
val randomSku = randomAlphanumeric(10)
val price = randomPrice()

val productCreateRequest = ProductCreateRequest(
newProduct = UpdatedProduct(
name = "Product $i ${randomAlphanumeric(8)}",
sku = randomSku,
price = price,
compareToPrice = 2 * price,
enabled = true,
quantity = 10,
attributes = listOf(
AttributeValue.createBrandAttributeValue(randomBrand),
),
options = listOf(
ProductOption.createSelectOption(
name = "Color",
choices = listOf(
ProductOptionChoice("Black"),
ProductOptionChoice("White"),
ProductOptionChoice("Yellow"),
ProductOptionChoice("Red")
),
defaultChoice = 0,
required = true
)
)
)
)
val productCreateResult = apiClient.createProduct(productCreateRequest)
assertTrue(productCreateResult.id > 0)

skus.add(randomSku)
brands.add(randomBrand)
}

return BrandedProductsCreateResult(
brandNames = brands,
skus = skus,
)
}

data class BrandedProductsCreateResult(
val brandNames: List<String>,
val skus: List<String>,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ val nullablePropertyRules: List<NullablePropertyRule<*, *>> = listOf(
productReviewMassUpdateRequestNullablePropertyRules,
productReviewSearchRequestNullablePropertyRules,
fetchedCustomersConfigNullablePropertyRules,
brandsSearchRequestNullablePropertyRules,
fetchedBrandNullablePropertyRules,
).flatten()

sealed class NullablePropertyRule<T, R>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.ecwid.apiclient.v3.rule.nullablepropertyrules

import com.ecwid.apiclient.v3.dto.brand.request.BrandsSearchRequest
import com.ecwid.apiclient.v3.rule.NullablePropertyRule
import com.ecwid.apiclient.v3.rule.NullablePropertyRule.AllowNullable

val brandsSearchRequestNullablePropertyRules: List<NullablePropertyRule<*, *>> = listOf(
AllowNullable(BrandsSearchRequest.ByFilters::lang),
AllowNullable(BrandsSearchRequest.ByFilters::hiddenBrands),
AllowNullable(BrandsSearchRequest.ByFilters::baseUrl),
AllowNullable(BrandsSearchRequest.ByFilters::cleanUrls),
AllowNullable(BrandsSearchRequest.ByFilters::sortBy),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.ecwid.apiclient.v3.rule.nullablepropertyrules

import com.ecwid.apiclient.v3.dto.brand.result.FetchedBrand
import com.ecwid.apiclient.v3.rule.NullablePropertyRule
import com.ecwid.apiclient.v3.rule.NullablePropertyRule.AllowNullable

val fetchedBrandNullablePropertyRules: List<NullablePropertyRule<*, *>> = listOf(
AllowNullable(FetchedBrand::nameTranslated),
AllowNullable(FetchedBrand::productsFilteredByBrandUrl),
)
4 changes: 2 additions & 2 deletions src/test/kotlin/com/ecwid/apiclient/v3/util/OrderUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ fun generateTestOrder(): UpdatedOrder {
shippingMethodName = "Method " + randomAlphanumeric(8),
shippingRate = randomPrice(),
estimatedTransitTime = "Estimates " + randomAlphanumeric(8),
isPickup = false,
isPickup = true,
pickupInstruction = "Instruction " + randomAlphanumeric(64),
fulfillmentType = FulfillmentType.SHIPPING
fulfillmentType = FulfillmentType.PICKUP
),
taxesOnShipping = listOf(),
handlingFee = UpdatedOrder.HandlingFee(
Expand Down
6 changes: 3 additions & 3 deletions src/test/resources/test.properties.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# don't use id of store that contains valuable and useful data! tests can modify or even delete store data
storeId=
apiToken=
apiHost=app.ecwid.com
storeId=1754
apiToken=secret_ZVk1LmWMfv8cnFy6bgQrvnyZSpBGjwsz
apiHost=app.local.ecwid.com
apiPort=443
Loading