Skip to content

Commit 062c3dc

Browse files
committed
[#370] Added error message parsing for missing user
Fixes #370
1 parent ed1815c commit 062c3dc

File tree

3 files changed

+123
-1
lines changed

3 files changed

+123
-1
lines changed

src/main/kotlin/dev/hossain/githubstats/util/ErrorInfo.kt

+25-1
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,42 @@ data class ErrorThreshold(
2424
/**
2525
* Error response from GitHub API.
2626
*
27-
* Sample error message.
27+
* Sample error messages.
2828
* ```json
2929
* {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
3030
*
3131
* {"message":"Validation Failed","errors":[{"resource":"Issue","code":"missing_field","field":"title"}],"documentation_url":"https://docs.github.com/rest/reference/issues#create-an-issue"}
3232
*
3333
* {"message":"Not Found","documentation_url":"https://docs.github.com/rest/pulls/pulls#get-a-pull-request","status":"404"}
34+
*
35+
* {"message":"Validation Failed","errors":[{"message":"The listed users cannot be searched either because the users do not exist or you do not have permission to view the users.","resource":"Search","field":"q","code":"invalid"}],"documentation_url":"https://docs.github.com/v3/search/","status":"422"}
3436
* ```
3537
*/
3638
@JsonClass(generateAdapter = true)
3739
data class GithubError(
3840
@Json(name = "message") val message: String,
3941
@Json(name = "documentation_url") val documentationUrl: String,
4042
@Json(name = "status") val status: Int? = null,
43+
@Json(name = "errors") val errors: List<GithubErrorDetail> = emptyList(),
44+
)
45+
46+
/**
47+
* Error details from GitHub API.
48+
*
49+
* Example error detail:
50+
* ```json
51+
* {
52+
* "resource": "Search",
53+
* "code": "invalid",
54+
* "message": "The listed users cannot be searched either because the users do not exist or you do not have permission to view the users.",
55+
* "field": "q"
56+
* }
57+
* ```
58+
*/
59+
@JsonClass(generateAdapter = true)
60+
data class GithubErrorDetail(
61+
@Json(name = "resource") val resource: String,
62+
@Json(name = "code") val code: String,
63+
@Json(name = "field") val field: String,
64+
@Json(name = "message") val message: String,
4165
)

src/main/kotlin/dev/hossain/githubstats/util/ErrorProcessor.kt

+30
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,36 @@ class ErrorProcessor {
2121
* ```
2222
*/
2323
private const val TOKEN_ERROR_MESSAGE = "Bad credentials"
24+
25+
/**
26+
* Error message when search query is invalid.
27+
*
28+
* Sample error message.
29+
* ```json
30+
* {"message":"Validation Failed","errors":[{"message":"The listed users cannot be searched.","resource":"Search","field":"q","code":"invalid"}],"documentation_url":"https://docs.github.com/v3/search/","status":"422"}
31+
* ```
32+
*/
33+
private const val VALIDATION_FAILED_ERROR_MESSAGE = "Validation Failed"
34+
35+
/**
36+
* Resource type for search error.
37+
*/
38+
private const val RESOURCE_TYPE_SEARCH = "Search"
39+
40+
/**
41+
* Check if user is missing in the search query.
42+
*/
43+
fun isUserMissingError(githubError: GithubError?): Boolean {
44+
if (githubError == null || githubError.message != VALIDATION_FAILED_ERROR_MESSAGE) {
45+
return false
46+
}
47+
48+
return githubError.errors.any {
49+
it.resource == RESOURCE_TYPE_SEARCH &&
50+
// Yes, hardcoding the server message string to avoid any false positive
51+
it.message.contains("users cannot be searched")
52+
}
53+
}
2454
}
2555

2656
/**

src/test/kotlin/dev/hossain/githubstats/util/ErrorProcessorTest.kt

+68
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,72 @@ class ErrorProcessorTest {
117117
assertThat(errorInfo.errorMessage).contains("HTTP 404")
118118
assertThat(errorInfo.githubError).isNull()
119119
}
120+
121+
@Test
122+
fun `getDetailedError - given HttpException with JSON github errors list - returns ErrorInfo processed data with errors`() {
123+
// language=JSON
124+
val jsonErrorBody =
125+
"""
126+
{
127+
"message": "Validation Failed",
128+
"errors": [
129+
{
130+
"message": "The listed users cannot be searched either because the users do not exist or you do not have permission to view the users.",
131+
"resource": "Search",
132+
"field": "q",
133+
"code": "invalid"
134+
}
135+
],
136+
"documentation_url": "https://docs.github.com/v3/search/",
137+
"status": "422"
138+
}
139+
""".trimIndent()
140+
val httpException = HttpException(Response.error<Any>(422, jsonErrorBody.toResponseBody("application/json".toMediaTypeOrNull())))
141+
val errorProcessor = ErrorProcessor()
142+
143+
val errorInfo = errorProcessor.getDetailedError(httpException)
144+
145+
assertThat(errorInfo).isInstanceOf(ErrorInfo::class.java)
146+
assertThat(errorInfo.githubError?.message).isEqualTo("Validation Failed")
147+
assertThat(errorInfo.githubError?.status).isEqualTo(422)
148+
assertThat(errorInfo.githubError?.errors).isNotEmpty()
149+
150+
val githubErrorDetail = errorInfo.githubError?.errors?.get(0)!!
151+
assertThat(
152+
githubErrorDetail.message,
153+
).isEqualTo(
154+
"The listed users cannot be searched either because the users do not exist or you do not have permission to view the users.",
155+
)
156+
assertThat(githubErrorDetail.resource).isEqualTo("Search")
157+
assertThat(githubErrorDetail.field).isEqualTo("q")
158+
assertThat(githubErrorDetail.code).isEqualTo("invalid")
159+
}
160+
161+
@Test
162+
fun `getDetailedError - given HttpException with JSON github error user not found - validates user not found`() {
163+
// language=JSON
164+
val jsonErrorBody =
165+
"""
166+
{
167+
"message": "Validation Failed",
168+
"errors": [
169+
{
170+
"message": "The listed users cannot be searched either because the users do not exist or you do not have permission to view the users.",
171+
"resource": "Search",
172+
"field": "q",
173+
"code": "invalid"
174+
}
175+
],
176+
"documentation_url": "https://docs.github.com/v3/search/",
177+
"status": "422"
178+
}
179+
""".trimIndent()
180+
val httpException = HttpException(Response.error<Any>(422, jsonErrorBody.toResponseBody("application/json".toMediaTypeOrNull())))
181+
val errorProcessor = ErrorProcessor()
182+
183+
val errorInfo = errorProcessor.getDetailedError(httpException)
184+
185+
assertThat(errorInfo).isInstanceOf(ErrorInfo::class.java)
186+
assertThat(ErrorProcessor.isUserMissingError(errorInfo.githubError)).isTrue()
187+
}
120188
}

0 commit comments

Comments
 (0)