Skip to content

Commit 7b48b44

Browse files
Added ApiHttpMethod
1 parent acc7dd7 commit 7b48b44

File tree

3 files changed

+64
-49
lines changed

3 files changed

+64
-49
lines changed

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

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,22 @@ package scommons.api.http
22

33
import play.api.libs.json._
44
import scommons.api.http.ApiHttpClient._
5+
import scommons.api.http.ApiHttpMethod._
56

6-
import scala.concurrent.{ExecutionContext, Future}
77
import scala.concurrent.duration._
8+
import scala.concurrent.{ExecutionContext, Future}
89

910
abstract class ApiHttpClient(baseUrl: String,
10-
defaultTimeout: FiniteDuration = 30.seconds)(implicit ec: ExecutionContext) {
11+
defaultTimeout: FiniteDuration = 30.seconds
12+
)(implicit ec: ExecutionContext) {
1113

1214
def execGet[R](url: String,
1315
params: List[(String, String)] = Nil,
1416
headers: List[(String, String)] = Nil,
1517
timeout: FiniteDuration = defaultTimeout
1618
)(implicit jsonReads: Reads[R]): Future[R] = {
1719

18-
exec[String, R]("GET", url, params, headers, None, timeout)
20+
exec[String](GET, url, params, headers, None, timeout).map(parseResponse[R])
1921
}
2022

2123
def execPost[D, R](url: String,
@@ -25,7 +27,7 @@ abstract class ApiHttpClient(baseUrl: String,
2527
timeout: FiniteDuration = defaultTimeout
2628
)(implicit jsonWrites: Writes[D], jsonReads: Reads[R]): Future[R] = {
2729

28-
exec("POST", url, params, headers, Some(data), timeout)
30+
exec(POST, url, params, headers, Some(data), timeout).map(parseResponse[R])
2931
}
3032

3133
def execPut[D, R](url: String,
@@ -35,7 +37,7 @@ abstract class ApiHttpClient(baseUrl: String,
3537
timeout: FiniteDuration = defaultTimeout
3638
)(implicit jsonWrites: Writes[D], jsonReads: Reads[R]): Future[R] = {
3739

38-
exec("PUT", url, params, headers, Some(data), timeout)
40+
exec(PUT, url, params, headers, Some(data), timeout).map(parseResponse[R])
3941
}
4042

4143
def execDelete[D, R](url: String,
@@ -45,29 +47,32 @@ abstract class ApiHttpClient(baseUrl: String,
4547
timeout: FiniteDuration = defaultTimeout
4648
)(implicit jsonWrites: Writes[D], jsonReads: Reads[R]): Future[R] = {
4749

48-
exec("DELETE", url, params, headers, data, timeout)
50+
exec(DELETE, url, params, headers, data, timeout).map(parseResponse[R])
4951
}
5052

51-
private def exec[T, R](method: String,
52-
url: String,
53-
params: List[(String, String)],
54-
headers: List[(String, String)],
55-
data: Option[T],
56-
timeout: FiniteDuration
57-
)(implicit jsonWrites: Writes[T], jsonReads: Reads[R]): Future[R] = {
53+
private def exec[T](method: ApiHttpMethod,
54+
url: String,
55+
params: List[(String, String)],
56+
headers: List[(String, String)],
57+
data: Option[T],
58+
timeout: FiniteDuration
59+
)(implicit jsonWrites: Writes[T]): Future[ApiHttpResponse] = {
5860

5961
val targetUrl = getTargetUrl(baseUrl, url)
6062

6163
execute(
62-
method = method,
64+
method = method.toString,
6365
targetUrl = targetUrl,
6466
params = params,
6567
headers = headers,
6668
jsonBody = data.map { d =>
6769
Json.stringify(Json.toJson(d))
6870
},
6971
timeout = timeout
70-
).map(parseResponse[R](targetUrl, _))
72+
).map {
73+
case None => throw ApiHttpTimeoutException(targetUrl)
74+
case Some(resp) => resp
75+
}
7176
}
7277

7378
protected def execute(method: String,
@@ -81,21 +86,17 @@ abstract class ApiHttpClient(baseUrl: String,
8186

8287
object ApiHttpClient {
8388

84-
def parseResponse[R](url: String, response: Option[ApiHttpResponse])
85-
(implicit jsonReads: Reads[R]): R = response match {
89+
def parseResponse[R](resp: ApiHttpResponse)(implicit jsonReads: Reads[R]): R = {
90+
val body = resp.body
8691

87-
case None => throw ApiHttpTimeoutException(url)
88-
89-
case Some(res) if res.status <= 299 =>
90-
val body = res.body
92+
if (resp.status <= 299) {
9193
Json.parse(body).validate[R] match {
9294
case JsSuccess(data, _) => data
9395
case JsError(error) =>
94-
throw ApiHttpStatusException(s"Fail to parse http response, error: $error", res)
96+
throw ApiHttpStatusException(s"Fail to parse http response, error: $error", resp)
9597
}
96-
97-
case Some(other) =>
98-
val body = other.body
98+
}
99+
else {
99100
val maybeData =
100101
if (body.trim.startsWith("{")) {
101102
Json.parse(body).validate[R] match {
@@ -107,8 +108,9 @@ object ApiHttpClient {
107108

108109
maybeData match {
109110
case Some(data) => data
110-
case None => throw ApiHttpStatusException("Received error response", other)
111+
case None => throw ApiHttpStatusException("Received error response", resp)
111112
}
113+
}
112114
}
113115

114116
def queryParams(params: (String, Option[_])*): List[(String, String)] = params.collect {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package scommons.api.http
2+
3+
sealed trait ApiHttpMethod
4+
5+
object ApiHttpMethod {
6+
7+
case object GET extends ApiHttpMethod
8+
case object POST extends ApiHttpMethod
9+
case object PUT extends ApiHttpMethod
10+
case object DELETE extends ApiHttpMethod
11+
}

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

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,27 @@ class ApiHttpClientSpec extends AsyncFlatSpec
4343
}
4444
}
4545

46+
it should "fail if timeout when execute request" in {
47+
//given
48+
val url = "/api/get/url"
49+
val execute = stubExec
50+
val client = new TestHttpClient(execute)
51+
52+
execute.when(*, *, *, *, *, *).returns(Future.successful(None))
53+
54+
//when
55+
client.execGet[List[TestRespData]](url, params, headers).failed.map { ex =>
56+
//then
57+
execute.verify("GET", s"$baseUrl$url", params, headers, None, defaultTimeout)
58+
59+
ex shouldBe ApiHttpTimeoutException(s"$baseUrl$url")
60+
61+
val message = ex.getMessage
62+
message should include("Request timed out, unable to get timely response")
63+
message should include(url)
64+
}
65+
}
66+
4667
it should "execute request and use defaultTimeout" in {
4768
//given
4869
val url = s"/api/get/url"
@@ -141,25 +162,6 @@ class ApiHttpClientSpec extends AsyncFlatSpec
141162
}
142163
}
143164

144-
it should "fail if timeout when parseResponse" in {
145-
//given
146-
val url = s"/some/url"
147-
148-
//when
149-
val ex = the[ApiHttpTimeoutException] thrownBy {
150-
ApiHttpClient.parseResponse[TestRespData](url, None)
151-
}
152-
153-
//then
154-
inside(ex) { case ApiHttpTimeoutException(resUrl) =>
155-
resUrl shouldBe url
156-
}
157-
158-
val message = ex.getMessage
159-
message should include("Request timed out, unable to get timely response")
160-
message should include(url)
161-
}
162-
163165
it should "fail if invalid json when parseResponse" in {
164166
//given
165167
val url = s"/some/url"
@@ -169,7 +171,7 @@ class ApiHttpClientSpec extends AsyncFlatSpec
169171

170172
//when
171173
val ex = the[ApiHttpStatusException] thrownBy {
172-
ApiHttpClient.parseResponse[TestRespData](url, Some(response))
174+
ApiHttpClient.parseResponse[TestRespData](response)
173175
}
174176

175177
//then
@@ -199,7 +201,7 @@ class ApiHttpClientSpec extends AsyncFlatSpec
199201

200202
//when
201203
val ex = the[ApiHttpStatusException] thrownBy {
202-
ApiHttpClient.parseResponse[List[TestRespData]](url, Some(response))
204+
ApiHttpClient.parseResponse[List[TestRespData]](response)
203205
}
204206

205207
//then
@@ -224,7 +226,7 @@ class ApiHttpClientSpec extends AsyncFlatSpec
224226
val response = ApiHttpResponse(url, 200, Map.empty, stringify(toJson(respData)))
225227

226228
//when
227-
val result = ApiHttpClient.parseResponse[List[TestRespData]](url, Some(response))
229+
val result = ApiHttpClient.parseResponse[List[TestRespData]](response)
228230

229231
//then
230232
result shouldBe respData
@@ -237,7 +239,7 @@ class ApiHttpClientSpec extends AsyncFlatSpec
237239
val response = ApiHttpResponse(url, 500, Map.empty, stringify(toJson(respData)))
238240

239241
//when
240-
val result = ApiHttpClient.parseResponse[TestRespData](url, Some(response))
242+
val result = ApiHttpClient.parseResponse[TestRespData](response)
241243

242244
//then
243245
result shouldBe respData

0 commit comments

Comments
 (0)