Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### Next release
- Fix: Align mobile SDK size recommendation API requests with web implementation

### 2.12.2
- Fix: Display proper size recommendation text

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ internal data class BodyProfileRecommendedSizeParams(
*/
fun paramsToMap(): Map<String, Any> {
return emptyMap<String, Any>()
.plus(
mapOf("app_origin" to 2),

Copilot AI Aug 3, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number 2 should be replaced with a named constant to improve code readability and maintainability. Consider defining a constant like APP_ORIGIN_ANDROID = 2.

Suggested change
mapOf("app_origin" to 2),
mapOf("app_origin" to APP_ORIGIN_ANDROID),

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikhail-yevstratiev resolve the comment

)
.plus(
mapOf(PARAM_BODY_DATA to createBodyDataParams()),
)
Expand All @@ -47,8 +50,40 @@ internal data class BodyProfileRecommendedSizeParams(
)
}

private fun createItemsParams(): Map<String, Any?> {
return emptyMap<String, Any?>()
fun paramsToMapShoe(): Map<String, Any> {
return emptyMap<String, Any>()
.plus(
mapOf(PARAM_BODY_DATA to createBodyDataParams()),
)
.plus(
mapOf(PARAM_USER_GENDER to userBodyProfile.gender),
)
.plus(
mapOf(PARAM_USER_HEIGHT to userBodyProfile.height),
)
.plus(
userBodyProfile.weight.toFloatOrNull()?.let { mapOf(PARAM_USER_WEIGHT to it) }
.orEmpty(),
)
.plus(
mapOf(PARAM_USER_AGE to userBodyProfile.age),
)
.plus(
createItemsParams()
)
.plus(
mapOf(PARAM_FOOTWEAR_DATA to userBodyProfile.footwearData),
)
.plus(
mapOf(PARAM_ADDITIONAL_INFO to createAdditionalInfoParams()),
)
.plus(
mapOf(PARAM_PRODUCT_NAME to storeProduct.name),
)
}

private fun createItemsParams(): Map<String, Any> {
return emptyMap<String, Any>()
.plus(
mapOf(PARAM_ITEM_SIZES to createItemSizesParams()),
)
Expand Down Expand Up @@ -80,6 +115,12 @@ internal data class BodyProfileRecommendedSizeParams(
measurement.name to measurement.millimeter
}
}
val modelInfo = storeProduct.storeProductMeta?.additionalInfo?.modelInfo
// Convert all keys in the map to snake_case
?.mapKeys { (key, _) ->
key.replace(Regex("([a-z])([A-Z])"), "$1_$2").lowercase()
} ?: JSONObject.NULL

return emptyMap<String, Any?>()
.plus(
mapOf(PARAM_BRAND to (brand ?: "")),
Expand All @@ -93,13 +134,10 @@ internal data class BodyProfileRecommendedSizeParams(
mapOf(PARAM_SIZES to (sizeHashMap ?: mutableMapOf())),
)
.plus(
mapOf(
PARAM_MODEL_INFO to
(storeProduct.storeProductMeta?.additionalInfo?.modelInfo ?: JSONObject.NULL),
),
mapOf(PARAM_MODEL_INFO to modelInfo),
)
.plus(
mapOf(PARAM_GENDER to userBodyProfile.gender),
mapOf(PARAM_GENDER to (storeProduct.storeProductMeta?.additionalInfo?.gender ?: "male"),),
)
.plus(
mapOf(
Expand Down Expand Up @@ -137,6 +175,10 @@ internal data class BodyProfileRecommendedSizeParams(
)
}.orEmpty(),
)
// Convert all keys in the map to snake_case
.mapKeys { (key, _) ->
key.replace(Regex("([a-z])([A-Z])"), "$1_$2").lowercase()
}
}

/**
Expand All @@ -155,21 +197,23 @@ internal data class BodyProfileRecommendedSizeParams(
}

private companion object {
const val PARAM_ADDITIONAL_INFO = "additionalInfo"
const val PARAM_BODY_DATA = "bodyData"
const val PARAM_ITEM_SIZES = "itemSizesOrig"
const val PARAM_ADDITIONAL_INFO = "additional_info"
const val PARAM_BODY_DATA = "body_data"
const val PARAM_ITEM_SIZES = "item_sizes_orig"
const val PARAM_ITEMS = "items"
const val PARAM_PRODUCT_TYPE = "productType"
const val PARAM_USER_GENDER = "userGender"
const val PARAM_USER_HEIGHT = "userHeight"
const val PARAM_USER_WEIGHT = "userWeight"
const val PARAM_USER_AGE = "userAge"
const val PARAM_EXTERNAL_PRODUCT_ID = "extProductId"
const val PARAM_PRODUCT_TYPE = "product_type"
const val PARAM_USER_GENDER = "user_gender"
const val PARAM_USER_HEIGHT = "user_height"
const val PARAM_USER_WEIGHT = "user_weight"
const val PARAM_USER_AGE = "user_age"
const val PARAM_EXTERNAL_PRODUCT_ID = "ext_product_id"
const val PARAM_FOOTWEAR_DATA = "footwear_data"
const val PARAM_PRODUCT_NAME = "product_name"

const val PARAM_BRAND = "brand"
const val PARAM_FIT = "fit"
const val PARAM_SIZES = "sizes"
const val PARAM_MODEL_INFO = "modelInfo"
const val PARAM_MODEL_INFO = "model_info"
const val PARAM_GENDER = "gender"
const val PARAM_STYLE = "style"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ class UserBodyProfileJsonParser : VirtusizeJsonParser<UserBodyProfile> {
val height = json.optInt(FIELD_HEIGHT)
val weight = json.optString(FIELD_WEIGHT)
var bodyData = setOf<Measurement>()
var footwearData = mapOf<String, Any>()
json.optJSONObject(FIELD_BODY_DATA)?.let { bodyDataJsonObject ->
bodyData = JsonUtils.jsonObjectToMeasurements(bodyDataJsonObject)
}
json.optJSONObject(FIELD_FOOTWEAR_DATA)?.let { footwearDataJsonObject ->
footwearData = JsonUtils.jsonObjectToMap(footwearDataJsonObject)
}
if (age == 0 || height == 0 || weight.isBlank() || bodyData.isEmpty()) {
return null
}
return UserBodyProfile(gender, age, height, weight, bodyData)
return UserBodyProfile(gender, age, height, weight, bodyData, footwearData)
}

companion object {
Expand All @@ -26,5 +30,6 @@ class UserBodyProfileJsonParser : VirtusizeJsonParser<UserBodyProfile> {
private const val FIELD_HEIGHT = "height"
private const val FIELD_WEIGHT = "weight"
private const val FIELD_BODY_DATA = "bodyData"
private const val FIELD_FOOTWEAR_DATA = "footwearData"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ data class UserBodyProfile(
val height: Int,
val weight: String,
val bodyData: Set<Measurement>,
val footwearData: Map<String, Any>,
)
Original file line number Diff line number Diff line change
Expand Up @@ -428,4 +428,26 @@ object VirtusizeApi {
.toString()
return ApiRequest(url, HttpMethod.POST, bodyProfileRecommendedSizeParams.paramsToMap())
}

/**
* Gets a API request for getting the recommended shoe size based on the user's body profile
* @param productTypes the list of available [ProductType]
* @param storeProduct [Product]
* @param userBodyProfile [UserBodyProfile]
* @see ApiRequest
*/
fun getShoeSize(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getShoeSize -> getShoeSizeRecommendation

productTypes: List<ProductType>,
storeProduct: Product,
userBodyProfile: UserBodyProfile,
): ApiRequest {
val bodyProfileRecommendedSizeParams =
BodyProfileRecommendedSizeParams(productTypes, storeProduct, userBodyProfile)
val url =
Uri.parse("${environment.sizeRecommendationApiBaseUrl()}${VirtusizeEndpoint.GetShoeSize.path}")
.buildUpon()
.build()
.toString()
return ApiRequest(url, HttpMethod.POST, bodyProfileRecommendedSizeParams.paramsToMapShoe())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ sealed interface VirtusizeEndpoint {
override val path: String = "/item"
}

data object GetShoeSize : VirtusizeEndpoint {
override val path: String = "/shoe"
}

data object LatestAoyamaVersion : VirtusizeEndpoint {
override val path: String = "/a/aoyama/latest.txt"
}
Expand Down
Loading
Loading