Skip to content

Commit 26be123

Browse files
fix: correctly parse eveara errorfield
1 parent 73f185c commit 26be123

File tree

2 files changed

+288
-1
lines changed
  • newm-server/src

2 files changed

+288
-1
lines changed
Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,100 @@
11
package io.newm.server.features.distribution.model
22

3+
import kotlinx.serialization.KSerializer
34
import kotlinx.serialization.SerialName
45
import kotlinx.serialization.Serializable
6+
import kotlinx.serialization.descriptors.SerialDescriptor
7+
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
8+
import kotlinx.serialization.encoding.Decoder
9+
import kotlinx.serialization.encoding.Encoder
10+
import kotlinx.serialization.json.JsonDecoder
11+
import kotlinx.serialization.json.JsonEncoder
12+
import kotlinx.serialization.json.JsonObject
13+
import kotlinx.serialization.json.JsonPrimitive
14+
import kotlinx.serialization.json.jsonPrimitive
515

16+
/**
17+
* Represents an error field from the Eveara API validation response.
18+
* The message field can be either a simple string or a complex object.
19+
*/
620
@Serializable
721
data class ErrorField(
822
@SerialName("fields")
923
val fields: String? = null,
1024
@SerialName("message")
11-
val message: String? = null,
25+
val message: ErrorFieldMessage? = null,
1226
)
27+
28+
/**
29+
* Represents the message content which can be either a simple string
30+
* or a complex nested message object from Eveara API.
31+
*/
32+
@Serializable(with = ErrorFieldMessageSerializer::class)
33+
sealed class ErrorFieldMessage {
34+
/**
35+
* Simple string message.
36+
*/
37+
data class SimpleMessage(
38+
val value: String
39+
) : ErrorFieldMessage()
40+
41+
/**
42+
* Complex message object containing nested details.
43+
* Example:
44+
* {
45+
* "message": ["This album is already distributed..."],
46+
* "success": false,
47+
* "errorFields": "date_original_release"
48+
* }
49+
*/
50+
@Serializable
51+
data class ComplexMessage(
52+
@SerialName("message")
53+
val messages: List<String>? = null,
54+
@SerialName("success")
55+
val success: Boolean? = null,
56+
@SerialName("errorFields")
57+
val errorFields: String? = null,
58+
) : ErrorFieldMessage()
59+
}
60+
61+
/**
62+
* Custom serializer for [ErrorFieldMessage] that handles both string and object formats.
63+
*/
64+
object ErrorFieldMessageSerializer : KSerializer<ErrorFieldMessage> {
65+
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ErrorFieldMessage")
66+
67+
override fun deserialize(decoder: Decoder): ErrorFieldMessage {
68+
val jsonDecoder = decoder as? JsonDecoder
69+
?: throw IllegalArgumentException("This serializer only works with JSON")
70+
val element = jsonDecoder.decodeJsonElement()
71+
72+
return when (element) {
73+
is JsonPrimitive -> ErrorFieldMessage.SimpleMessage(element.content)
74+
75+
is JsonObject -> jsonDecoder.json.decodeFromJsonElement(
76+
ErrorFieldMessage.ComplexMessage.serializer(),
77+
element
78+
)
79+
80+
else -> throw IllegalArgumentException("Unsupported ErrorFieldMessage format: $element")
81+
}
82+
}
83+
84+
override fun serialize(
85+
encoder: Encoder,
86+
value: ErrorFieldMessage
87+
) {
88+
val jsonEncoder = encoder as? JsonEncoder
89+
?: throw IllegalArgumentException("This serializer only works with JSON")
90+
91+
when (value) {
92+
is ErrorFieldMessage.SimpleMessage -> jsonEncoder.encodeJsonElement(JsonPrimitive(value.value))
93+
94+
is ErrorFieldMessage.ComplexMessage -> jsonEncoder.encodeSerializableValue(
95+
ErrorFieldMessage.ComplexMessage.serializer(),
96+
value
97+
)
98+
}
99+
}
100+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package io.newm.server.features.distribution.model
2+
3+
import com.google.common.truth.Truth.assertThat
4+
import kotlinx.serialization.json.Json
5+
import org.junit.jupiter.api.Test
6+
7+
class ErrorFieldTest {
8+
private val json =
9+
Json {
10+
ignoreUnknownKeys = true
11+
explicitNulls = false
12+
isLenient = true
13+
}
14+
15+
@Test
16+
fun `test parsing ErrorField with simple string message`() {
17+
val jsonString =
18+
"""
19+
{
20+
"fields": "Album Status",
21+
"message": "Album Status is draft"
22+
}
23+
""".trimIndent()
24+
25+
val result: ErrorField = json.decodeFromString(jsonString)
26+
27+
assertThat(result.fields).isEqualTo("Album Status")
28+
assertThat(result.message).isInstanceOf(ErrorFieldMessage.SimpleMessage::class.java)
29+
val simpleMessage = result.message as ErrorFieldMessage.SimpleMessage
30+
assertThat(simpleMessage.value).isEqualTo("Album Status is draft")
31+
}
32+
33+
@Test
34+
fun `test parsing ErrorField with complex nested message object`() {
35+
val jsonString =
36+
"""
37+
{
38+
"message": {
39+
"message": [
40+
"This album is already distributed to some outlets with release date 20/01/2026. Please choose the same date or a date before this."
41+
],
42+
"success": false,
43+
"errorFields": "date_original_release"
44+
}
45+
}
46+
""".trimIndent()
47+
48+
val result: ErrorField = json.decodeFromString(jsonString)
49+
50+
assertThat(result.fields).isNull()
51+
assertThat(result.message).isInstanceOf(ErrorFieldMessage.ComplexMessage::class.java)
52+
val complexMessage = result.message as ErrorFieldMessage.ComplexMessage
53+
assertThat(complexMessage.messages).hasSize(1)
54+
assertThat(complexMessage.messages?.first()).contains("This album is already distributed")
55+
assertThat(complexMessage.success).isFalse()
56+
assertThat(complexMessage.errorFields).isEqualTo("date_original_release")
57+
}
58+
59+
@Test
60+
fun `test parsing ValidateData with mixed error_fields`() {
61+
val jsonString =
62+
"""
63+
{
64+
"album_status": {
65+
"status_code": 1021,
66+
"status_name": "Draft"
67+
},
68+
"error_fields": [
69+
{
70+
"message": {
71+
"message": [
72+
"This album is already distributed to some outlets with release date 20/01/2026. Please choose the same date or a date before this."
73+
],
74+
"success": false,
75+
"errorFields": "date_original_release"
76+
}
77+
},
78+
{
79+
"fields": "Album Status",
80+
"message": "Album Status is draft"
81+
}
82+
]
83+
}
84+
""".trimIndent()
85+
86+
val result: ValidateData = json.decodeFromString(jsonString)
87+
88+
assertThat(result.albumStatus.statusCode).isEqualTo(1021)
89+
assertThat(result.albumStatus.statusName).isEqualTo("Draft")
90+
assertThat(result.errorFields).hasSize(2)
91+
92+
// First error field: complex message
93+
val firstError = result.errorFields!![0]
94+
assertThat(firstError.fields).isNull()
95+
assertThat(firstError.message).isInstanceOf(ErrorFieldMessage.ComplexMessage::class.java)
96+
val complexMessage = firstError.message as ErrorFieldMessage.ComplexMessage
97+
assertThat(complexMessage.messages).hasSize(1)
98+
assertThat(complexMessage.errorFields).isEqualTo("date_original_release")
99+
100+
// Second error field: simple message
101+
val secondError = result.errorFields!![1]
102+
assertThat(secondError.fields).isEqualTo("Album Status")
103+
assertThat(secondError.message).isInstanceOf(ErrorFieldMessage.SimpleMessage::class.java)
104+
val simpleMessage = secondError.message as ErrorFieldMessage.SimpleMessage
105+
assertThat(simpleMessage.value).isEqualTo("Album Status is draft")
106+
}
107+
108+
@Test
109+
fun `test parsing full ValidateAlbumResponse with error_fields`() {
110+
val jsonString =
111+
"""
112+
{
113+
"message": "You can't distribute this album now. Please enter all required details for this album",
114+
"success": true,
115+
"data": {
116+
"error_fields": [
117+
{
118+
"message": {
119+
"message": [
120+
"This album is already distributed to some outlets with release date 20/01/2026. Please choose the same date or a date before this."
121+
],
122+
"success": false,
123+
"errorFields": "date_original_release"
124+
}
125+
},
126+
{
127+
"fields": "Album Status",
128+
"message": "Album Status is draft"
129+
}
130+
],
131+
"album_status": {
132+
"status_code": 1021,
133+
"status_name": "Draft"
134+
}
135+
}
136+
}
137+
""".trimIndent()
138+
139+
val result: ValidateAlbumResponse = json.decodeFromString(jsonString)
140+
141+
assertThat(result.message).isEqualTo("You can't distribute this album now. Please enter all required details for this album")
142+
assertThat(result.success).isTrue()
143+
assertThat(result.validateData.albumStatus.statusCode).isEqualTo(1021)
144+
assertThat(result.validateData.errorFields).hasSize(2)
145+
146+
// Verify complex message
147+
val complexError = result.validateData.errorFields!![0]
148+
val complexMessage = complexError.message as ErrorFieldMessage.ComplexMessage
149+
assertThat(complexMessage.messages?.first()).contains("already distributed")
150+
151+
// Verify simple message
152+
val simpleError = result.validateData.errorFields!![1]
153+
val simpleMessage = simpleError.message as ErrorFieldMessage.SimpleMessage
154+
assertThat(simpleMessage.value).isEqualTo("Album Status is draft")
155+
}
156+
157+
@Test
158+
fun `test parsing ErrorField with null message`() {
159+
val jsonString =
160+
"""
161+
{
162+
"fields": "Some Field"
163+
}
164+
""".trimIndent()
165+
166+
val result: ErrorField = json.decodeFromString(jsonString)
167+
168+
assertThat(result.fields).isEqualTo("Some Field")
169+
assertThat(result.message).isNull()
170+
}
171+
172+
@Test
173+
fun `test parsing complex message with multiple messages in array`() {
174+
val jsonString =
175+
"""
176+
{
177+
"message": {
178+
"message": [
179+
"First error message",
180+
"Second error message",
181+
"Third error message"
182+
],
183+
"success": false,
184+
"errorFields": "some_field"
185+
}
186+
}
187+
""".trimIndent()
188+
189+
val result: ErrorField = json.decodeFromString(jsonString)
190+
191+
val complexMessage = result.message as ErrorFieldMessage.ComplexMessage
192+
assertThat(complexMessage.messages).hasSize(3)
193+
assertThat(complexMessage.messages).containsExactly(
194+
"First error message",
195+
"Second error message",
196+
"Third error message"
197+
)
198+
}
199+
}

0 commit comments

Comments
 (0)