Skip to content

Commit c36e3c2

Browse files
Added ApiHttpStatusException and ApiHttpTimeoutException
1 parent 37de5c5 commit c36e3c2

File tree

4 files changed

+69
-37
lines changed

4 files changed

+69
-37
lines changed

core/shared/src/main/scala/scommons/api/http/ApiHttpClient.scala

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ abstract class ApiHttpClient(baseUrl: String,
6767
Json.stringify(Json.toJson(d))
6868
},
6969
timeout = timeout
70-
).map(parseResponse(targetUrl, _))
70+
).map(parseResponse[R](targetUrl, _))
7171
}
7272

7373
protected def execute(method: String,
@@ -77,27 +77,21 @@ abstract class ApiHttpClient(baseUrl: String,
7777
jsonBody: Option[String],
7878
timeout: FiniteDuration
7979
): Future[Option[ApiHttpResponse]]
80+
}
81+
82+
object ApiHttpClient {
8083

81-
private[http] def parseResponse[R](url: String, response: Option[ApiHttpResponse])
82-
(implicit jsonReads: Reads[R]): R = response match {
84+
def parseResponse[R](url: String, response: Option[ApiHttpResponse])
85+
(implicit jsonReads: Reads[R]): R = response match {
8386

84-
case None =>
85-
throw new Exception(
86-
s"""Request timed out, unable to get timely response for:
87-
|$url""".stripMargin)
87+
case None => throw ApiHttpTimeoutException(url)
8888

8989
case Some(res) if res.status <= 299 =>
9090
val body = res.body
9191
Json.parse(body).validate[R] match {
9292
case JsSuccess(data, _) => data
9393
case JsError(error) =>
94-
val err =
95-
s"""Error parsing http response:
96-
|url: $url
97-
|status: ${res.status}
98-
|error: $error
99-
|body: $body""".stripMargin
100-
throw new Exception(err)
94+
throw ApiHttpStatusException(s"Fail to parse http response, error: $error", url, res.status, body)
10195
}
10296

10397
case Some(other) =>
@@ -113,17 +107,9 @@ abstract class ApiHttpClient(baseUrl: String,
113107

114108
maybeData match {
115109
case Some(data) => data
116-
case None =>
117-
throw new Exception(
118-
s"""Received error response:
119-
|url: $url
120-
|status: ${other.status}
121-
|body: $body""".stripMargin)
110+
case None => throw ApiHttpStatusException("Received error response", url, other.status, body)
122111
}
123112
}
124-
}
125-
126-
object ApiHttpClient {
127113

128114
def queryParams(params: (String, Option[_])*): List[(String, String)] = params.collect {
129115
case (p, Some(v)) => (p, v.toString)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package scommons.api.http
2+
3+
import scommons.api.http.ApiHttpStatusException._
4+
5+
case class ApiHttpStatusException(error: String,
6+
url: String,
7+
status: Int,
8+
body: String
9+
) extends RuntimeException(buildMessage(error, url, status, body))
10+
11+
object ApiHttpStatusException {
12+
13+
def buildMessage(error: String,
14+
url: String,
15+
status: Int,
16+
body: String): String = {
17+
18+
s"""$error
19+
| url: $url
20+
| status: $status
21+
| body: $body""".stripMargin
22+
}
23+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package scommons.api.http
2+
3+
case class ApiHttpTimeoutException(url: String)
4+
extends RuntimeException(
5+
s"""Request timed out, unable to get timely response for:
6+
| $url""".stripMargin
7+
)

core/shared/src/test/scala/scommons/api/http/ApiHttpClientSpec.scala

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package scommons.api.http
22

33
import org.scalamock.scalatest.AsyncMockFactory
4-
import org.scalatest.{AsyncFlatSpec, Matchers}
4+
import org.scalatest.{AsyncFlatSpec, Inside, Matchers}
55
import play.api.libs.json.Json.{stringify, toJson}
66
import play.api.libs.json._
77
import scommons.api.http.ApiHttpClient._
@@ -12,6 +12,7 @@ import scala.concurrent.duration._
1212

1313
class ApiHttpClientSpec extends AsyncFlatSpec
1414
with Matchers
15+
with Inside
1516
with AsyncMockFactory {
1617

1718
private val baseUrl = "http://test.api.client"
@@ -143,14 +144,17 @@ class ApiHttpClientSpec extends AsyncFlatSpec
143144
it should "fail if timeout when parseResponse" in {
144145
//given
145146
val url = s"/some/url"
146-
val client = new TestHttpClient(null)
147147

148148
//when
149-
val ex = the[Exception] thrownBy {
150-
client.parseResponse[TestRespData](url, None)
149+
val ex = the[ApiHttpTimeoutException] thrownBy {
150+
ApiHttpClient.parseResponse[TestRespData](url, None)
151151
}
152152

153153
//then
154+
inside(ex) { case ApiHttpTimeoutException(resUrl) =>
155+
resUrl shouldBe url
156+
}
157+
154158
val message = ex.getMessage
155159
message should include("Request timed out, unable to get timely response")
156160
message should include(url)
@@ -162,14 +166,22 @@ class ApiHttpClientSpec extends AsyncFlatSpec
162166
val statusCode = 200
163167
val data = """{"id": 1, "missing": "name"}"""
164168
val response = ApiHttpResponse(statusCode, data)
165-
val client = new TestHttpClient(null)
166169

167170
//when
168-
val ex = the[Exception] thrownBy {
169-
client.parseResponse[TestRespData](url, Some(response))
171+
val ex = the[ApiHttpStatusException] thrownBy {
172+
ApiHttpClient.parseResponse[TestRespData](url, Some(response))
170173
}
171174

172175
//then
176+
inside(ex) { case ApiHttpStatusException(error, resUrl, status, body) =>
177+
error shouldBe {
178+
"Fail to parse http response, error: List((/name,List(JsonValidationError(List(error.path.missing),WrappedArray()))))"
179+
}
180+
resUrl shouldBe url
181+
status shouldBe statusCode
182+
body shouldBe data
183+
}
184+
173185
val message = ex.getMessage
174186
message should include(s"url: $url")
175187
message should include(s"status: $statusCode")
@@ -183,14 +195,20 @@ class ApiHttpClientSpec extends AsyncFlatSpec
183195
val statusCode = 400
184196
val data = "testData"
185197
val response = ApiHttpResponse(statusCode, data)
186-
val client = new TestHttpClient(null)
187198

188199
//when
189-
val ex = the[Exception] thrownBy {
190-
client.parseResponse[List[TestRespData]](url, Some(response))
200+
val ex = the[ApiHttpStatusException] thrownBy {
201+
ApiHttpClient.parseResponse[List[TestRespData]](url, Some(response))
191202
}
192203

193204
//then
205+
inside(ex) { case ApiHttpStatusException(error, resUrl, status, body) =>
206+
error shouldBe "Received error response"
207+
resUrl shouldBe url
208+
status shouldBe statusCode
209+
body shouldBe data
210+
}
211+
194212
val message = ex.getMessage
195213
message should include(s"url: $url")
196214
message should include(s"status: $statusCode")
@@ -201,10 +219,9 @@ class ApiHttpClientSpec extends AsyncFlatSpec
201219
//given
202220
val respData = List(TestRespData(1, "test"))
203221
val response = ApiHttpResponse(200, stringify(toJson(respData)))
204-
val client = new TestHttpClient(null)
205222

206223
//when
207-
val result = client.parseResponse[List[TestRespData]](s"/api/url", Some(response))
224+
val result = ApiHttpClient.parseResponse[List[TestRespData]](s"/api/url", Some(response))
208225

209226
//then
210227
result shouldBe respData
@@ -214,10 +231,9 @@ class ApiHttpClientSpec extends AsyncFlatSpec
214231
//given
215232
val respData = TestRespData(1, "test")
216233
val response = ApiHttpResponse(500, stringify(toJson(respData)))
217-
val client = new TestHttpClient(null)
218234

219235
//when
220-
val result = client.parseResponse[TestRespData](s"/api/url", Some(response))
236+
val result = ApiHttpClient.parseResponse[TestRespData](s"/api/url", Some(response))
221237

222238
//then
223239
result shouldBe respData

0 commit comments

Comments
 (0)