Skip to content

Commit d9dea2e

Browse files
author
Vasily Karyaev
committed
Added NullableUpdatedValue wrapper
1 parent d1a4700 commit d9dea2e

8 files changed

Lines changed: 126 additions & 4 deletions

File tree

src/main/kotlin/com/ecwid/apiclient/v3/converter/FetchedProduct.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.ecwid.apiclient.v3.converter
22

3+
import com.ecwid.apiclient.v3.dto.common.NullableUpdatedValue
34
import com.ecwid.apiclient.v3.dto.product.enums.toAttributeValueAlias
45
import com.ecwid.apiclient.v3.dto.product.request.UpdatedProduct
56
import com.ecwid.apiclient.v3.dto.product.result.FetchedProduct
@@ -54,7 +55,7 @@ fun FetchedProduct.toUpdated(): UpdatedProduct {
5455
customPriceTiers = customPriceTiers?.map { it.toUpdated() },
5556
priceDefaultTier = priceDefaultTier,
5657
subscriptionSettings = subscriptionSettings?.toUpdated(),
57-
googleProductCategory = googleProductCategory,
58+
googleProductCategory = NullableUpdatedValue(googleProductCategory),
5859
productCondition = productCondition,
5960
externalReferenceId = externalReferenceId,
6061
customsHsTariffCode = customsHsTariffCode,
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.ecwid.apiclient.v3.dto.common
2+
3+
/**
4+
* Wrapper for request values, which can be sent as explicit nulls in resulting json.
5+
*/
6+
data class NullableUpdatedValue<T : Any>(
7+
val value: T?,
8+
)

src/main/kotlin/com/ecwid/apiclient/v3/dto/product/request/UpdatedProduct.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.ecwid.apiclient.v3.dto.product.request
22

3+
import com.ecwid.apiclient.v3.dto.common.NullableUpdatedValue
34
import com.ecwid.apiclient.v3.dto.common.ApiUpdatedDTO
45
import com.ecwid.apiclient.v3.dto.common.ApiUpdatedDTO.ModifyKind
56
import com.ecwid.apiclient.v3.dto.common.LocalizedValueMap
@@ -55,7 +56,7 @@ data class UpdatedProduct(
5556
val customPriceTiers: List<CustomPriceTier>? = null,
5657
val priceDefaultTier: Int? = null,
5758
val subscriptionSettings: SubscriptionSettings? = null,
58-
val googleProductCategory: Int? = null,
59+
val googleProductCategory: NullableUpdatedValue<Int>? = null,
5960
val productCondition: ProductCondition? = null,
6061
val externalReferenceId: String? = null,
6162
val customsHsTariffCode: String? = null,

src/main/kotlin/com/ecwid/apiclient/v3/jsontransformer/gson/GsonTransformer.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.ecwid.apiclient.v3.dto.product.result.GetProductFiltersResult.Product
44
import com.ecwid.apiclient.v3.exception.JsonDeserializationException
55
import com.ecwid.apiclient.v3.jsontransformer.JsonTransformer
66
import com.ecwid.apiclient.v3.jsontransformer.PolymorphicType
7+
import com.ecwid.apiclient.v3.jsontransformer.gson.typeadapters.GsonNullableUpdatedValueTypeAdapterFactory
78
import com.ecwid.apiclient.v3.jsontransformer.gson.typeadapters.GsonPolymorphicDeserializer
89
import com.ecwid.apiclient.v3.jsontransformer.gson.typeadapters.GsonProductFiltersDeserializer
910
import com.google.gson.*
@@ -18,6 +19,7 @@ class GsonTransformer(polymorphicTypes: List<PolymorphicType<*>>) : JsonTransfor
1819
gsonBuilder.registerTypeAdapter(polymorphType.rootClass, GsonPolymorphicDeserializer(polymorphType))
1920
}
2021
gsonBuilder.registerTypeAdapter(ProductFilters::class.java, GsonProductFiltersDeserializer())
22+
gsonBuilder.registerTypeAdapterFactory(GsonNullableUpdatedValueTypeAdapterFactory)
2123
}
2224
.create()
2325

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.ecwid.apiclient.v3.jsontransformer.gson.typeadapters
2+
3+
import com.ecwid.apiclient.v3.dto.common.NullableUpdatedValue
4+
import com.google.gson.Gson
5+
import com.google.gson.JsonElement
6+
import com.google.gson.JsonNull
7+
import com.google.gson.TypeAdapter
8+
import com.google.gson.TypeAdapterFactory
9+
import com.google.gson.reflect.TypeToken
10+
import com.google.gson.stream.JsonReader
11+
import com.google.gson.stream.JsonWriter
12+
import java.lang.reflect.ParameterizedType
13+
14+
15+
object GsonNullableUpdatedValueTypeAdapterFactory : TypeAdapterFactory {
16+
override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
17+
return if (type.rawType == NullableUpdatedValue::class.java) {
18+
@Suppress("UNCHECKED_CAST")
19+
GsonNullableUpdatedValueTypeAdapter(this, gson, type).nullSafe() as TypeAdapter<T>
20+
} else {
21+
null
22+
}
23+
}
24+
}
25+
26+
27+
private class GsonNullableUpdatedValueTypeAdapter(
28+
factory: TypeAdapterFactory,
29+
gson: Gson,
30+
typeToken: TypeToken<*>,
31+
) : TypeAdapter<NullableUpdatedValue<*>>() {
32+
33+
@Suppress("UNCHECKED_CAST")
34+
private val wrappedValueDelegateAdapter = gson.getDelegateAdapter(
35+
factory,
36+
TypeToken.get((typeToken.type as ParameterizedType).actualTypeArguments[0]),
37+
) as TypeAdapter<Any>
38+
39+
private val elementAdapter = gson.getAdapter(JsonElement::class.java)
40+
41+
override fun write(writer: JsonWriter, value: NullableUpdatedValue<*>) {
42+
val wrappedValue: Any? = value.value
43+
if (wrappedValue != null) {
44+
elementAdapter.write(writer, wrappedValueDelegateAdapter.toJsonTree(wrappedValue))
45+
} else {
46+
val originalSerializeNulls = writer.serializeNulls
47+
try {
48+
writer.serializeNulls = true
49+
elementAdapter.write(writer, JsonNull.INSTANCE)
50+
} finally {
51+
writer.serializeNulls = originalSerializeNulls
52+
}
53+
}
54+
}
55+
56+
override fun read(reader: JsonReader): NullableUpdatedValue<*> {
57+
throw UnsupportedOperationException()
58+
}
59+
}

src/test/kotlin/com/ecwid/apiclient/v3/entity/ProductsTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.ecwid.apiclient.v3.entity
22

33
import com.ecwid.apiclient.v3.converter.toUpdated
4+
import com.ecwid.apiclient.v3.dto.common.NullableUpdatedValue
45
import com.ecwid.apiclient.v3.dto.category.request.CategoriesSearchRequest
56
import com.ecwid.apiclient.v3.dto.category.request.CategoriesSearchRequest.ParentCategory
67
import com.ecwid.apiclient.v3.dto.category.request.CategoryCreateRequest
@@ -1425,7 +1426,7 @@ private fun generateTestProduct(categoryIds: List<Int> = listOf()): UpdatedProdu
14251426
"en" to "Subtitle"
14261427
),
14271428
subscriptionSettings = SubscriptionSettings(),
1428-
googleProductCategory = 632,
1429+
googleProductCategory = NullableUpdatedValue(632),
14291430
productCondition = ProductCondition.USED,
14301431
externalReferenceId = "EXT_ID_123",
14311432
customsHsTariffCode = randomAlphanumeric(10),

src/test/kotlin/com/ecwid/apiclient/v3/jsontransformer/gson/GsonTransformerTest.kt

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.ecwid.apiclient.v3.jsontransformer.gson
22

3+
import com.ecwid.apiclient.v3.dto.common.NullableUpdatedValue
34
import com.ecwid.apiclient.v3.dto.order.request.UpdatedOrder
5+
import com.ecwid.apiclient.v3.dto.product.request.UpdatedProduct
46
import com.google.gson.JsonParser
57
import org.junit.jupiter.api.Assertions.assertEquals
68
import org.junit.jupiter.api.Test
@@ -166,6 +168,47 @@ internal class GsonTransformerTest {
166168
transformer.serialize(order, orderExt)
167169
)
168170
}
171+
172+
@Test
173+
fun `test serialization with NullableUpdatedValue`() {
174+
var product = UpdatedProduct(
175+
name = "Test product",
176+
googleProductCategory = null,
177+
)
178+
179+
assertJsonEquals(
180+
"""
181+
{
182+
"name": "Test product"
183+
}
184+
""".trimIndent(),
185+
transformer.serialize(product, null)
186+
)
187+
188+
product = product.copy(googleProductCategory = NullableUpdatedValue(null))
189+
190+
assertJsonEquals(
191+
"""
192+
{
193+
"name": "Test product",
194+
"googleProductCategory": null
195+
}
196+
""".trimIndent(),
197+
transformer.serialize(product, null)
198+
)
199+
200+
product = product.copy(googleProductCategory = NullableUpdatedValue(666))
201+
202+
assertJsonEquals(
203+
"""
204+
{
205+
"name": "Test product",
206+
"googleProductCategory": 666
207+
}
208+
""".trimIndent(),
209+
transformer.serialize(product, null)
210+
)
211+
}
169212
}
170213

171214
private fun assertJsonEquals(expected: String, actual: String) {

src/test/kotlin/com/ecwid/apiclient/v3/util/FetchedUpdatedDTOsChecker.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.ecwid.apiclient.v3.util
22

3+
import com.ecwid.apiclient.v3.dto.common.NullableUpdatedValue
34
import com.ecwid.apiclient.v3.rule.NonUpdatablePropertyRule
45
import java.lang.reflect.Field
56
import java.lang.reflect.ParameterizedType
@@ -214,9 +215,15 @@ private fun checkField(
214215
}
215216
}
216217
else -> {
218+
val updatedDTOFieldClassCorrecterd = if (updatedDTOFieldClass == NullableUpdatedValue::class.java) {
219+
(updatedDTOField.genericType as ParameterizedType).actualTypeArguments[0] as Class<*>
220+
} else {
221+
updatedDTOFieldClass
222+
}
223+
217224
checkClassOrPrimitive(
218225
fetchedDTOClass = fetchedDTOFieldClass,
219-
updatedDTOClass = updatedDTOFieldClass,
226+
updatedDTOClass = updatedDTOFieldClassCorrecterd,
220227
parentFetchedDTOClass = fetchedDTOClass,
221228
parentUpdatedDTOClass = updatedDTOClass,
222229
fieldName = fieldName,

0 commit comments

Comments
 (0)