Skip to content

Commit 1a9728d

Browse files
author
Marius Posta
authored
bulk-cdk: make exception classifiers recursive (#45141)
1 parent d02d29c commit 1a9728d

File tree

4 files changed

+36
-27
lines changed

4 files changed

+36
-27
lines changed

airbyte-cdk/bulk/core/base/src/main/kotlin/io/airbyte/cdk/output/DefaultExceptionClassifier.kt

+6-18
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,12 @@ class DefaultExceptionClassifier(
2222
) : ExceptionClassifier {
2323

2424
override fun classify(e: Throwable): ConnectorError? {
25-
return when (val connectorErrorException: ConnectorErrorException? = unwind(e)) {
26-
is ConfigErrorException -> ConfigError(connectorErrorException.message!!)
27-
is TransientErrorException -> TransientError(connectorErrorException.message!!)
28-
is SystemErrorException -> SystemError(connectorErrorException.message)
29-
null -> null
25+
val unwound: Throwable? = ExceptionClassifier.unwind(e) { it is ConnectorErrorException }
26+
return when (unwound) {
27+
is ConfigErrorException -> ConfigError(unwound.message!!)
28+
is TransientErrorException -> TransientError(unwound.message!!)
29+
is SystemErrorException -> SystemError(unwound.message)
30+
else -> null
3031
}
3132
}
32-
33-
/** Recursively walks the causes of [e] and returns the last [ConnectorErrorException]. */
34-
fun unwind(e: Throwable): ConnectorErrorException? {
35-
var connectorErrorException: ConnectorErrorException? = null
36-
var unwound: Throwable? = e
37-
while (unwound != null) {
38-
if (unwound is ConnectorErrorException) {
39-
connectorErrorException = unwound
40-
}
41-
unwound = unwound.cause
42-
}
43-
return connectorErrorException
44-
}
4533
}

airbyte-cdk/bulk/core/base/src/main/kotlin/io/airbyte/cdk/output/ExceptionClassifier.kt

+12-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ interface ExceptionClassifier : Ordered {
1515
val orderValue: Int
1616

1717
override fun getOrder(): Int = orderValue
18+
19+
companion object {
20+
fun unwind(e: Throwable, stopUnwind: (Throwable) -> Boolean): Throwable? {
21+
var unwound = e
22+
while (!stopUnwind(unwound)) {
23+
unwound = unwound.cause ?: return null
24+
}
25+
return unwound
26+
}
27+
}
1828
}
1929

2030
/** Each [ConnectorError] subtype corresponds to a [AirbyteErrorTraceMessage.FailureType]. */
@@ -54,10 +64,8 @@ interface RuleBasedExceptionClassifier<T : RuleBasedExceptionClassifier.Rule> :
5464

5565
override fun classify(e: Throwable): ConnectorError? {
5666
for (rule in rules) {
57-
if (!rule.matches(e)) {
58-
continue
59-
}
60-
val message: String = rule.output ?: e.message ?: e.toString()
67+
val match: Throwable = ExceptionClassifier.unwind(e, rule::matches) ?: continue
68+
val message: String = rule.output ?: match.message ?: match.toString()
6169
val firstLine: String = if (rule.group == null) message else "${rule.group}: $message"
6270
val lines: List<String> = listOf(firstLine) + rule.referenceLinks
6371
val displayMessage: String = lines.joinToString(separator = "\n")

airbyte-cdk/bulk/core/base/src/test/kotlin/io/airbyte/cdk/output/RegexExceptionClassifierTest.kt

+12
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,16 @@ class RegexExceptionClassifierTest {
8080
classifier.classify(RuntimeException("barbaz")),
8181
)
8282
}
83+
84+
@Test
85+
fun testRecursiveRuleOrdering() {
86+
Assertions.assertEquals(
87+
ConfigError("grouped: has foo\nhttps://www.youtube.com/watch?v=xvFZjo5PgG0"),
88+
classifier.classify(RuntimeException("quux", RuntimeException("foobarbaz"))),
89+
)
90+
Assertions.assertEquals(
91+
TransientError("barbaz"),
92+
classifier.classify(RuntimeException("quux", RuntimeException("barbaz"))),
93+
)
94+
}
8395
}

airbyte-cdk/bulk/toolkits/extract-jdbc/src/main/kotlin/io/airbyte/cdk/output/JdbcExceptionClassifier.kt

+6-5
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,16 @@ class JdbcExceptionClassifier(
2929
}
3030

3131
override fun classify(e: Throwable): ConnectorError? {
32-
if (e !is SQLException) return null
32+
var match: SQLException =
33+
ExceptionClassifier.unwind(e) { it is SQLException } as? SQLException ?: return null
3334
val decoratedMessage: String =
3435
listOfNotNull(
35-
e.sqlState?.let { "State code: $it" },
36-
e.errorCode.takeIf { it != 0 }?.let { "Error code: $it" },
37-
e.message?.let { "Message: $it" },
36+
match.sqlState?.let { "State code: $it" },
37+
match.errorCode.takeIf { it != 0 }?.let { "Error code: $it" },
38+
match.message?.let { "Message: $it" },
3839
)
3940
.joinToString(separator = "; ")
40-
val decoratedException = SQLException(decoratedMessage, e.sqlState, e.errorCode)
41+
val decoratedException = SQLException(decoratedMessage, match.sqlState, match.errorCode)
4142
val ruleBasedMatch: ConnectorError? = super.classify(decoratedException)
4243
if (ruleBasedMatch != null) {
4344
return ruleBasedMatch

0 commit comments

Comments
 (0)