Skip to content

Commit 066d23c

Browse files
committed
Use http lib Request and Response types
1 parent 01709c7 commit 066d23c

File tree

4 files changed

+119
-99
lines changed

4 files changed

+119
-99
lines changed

rebar.config

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99

1010
{deps, [
1111
{gleam_stdlib, "0.10.0"},
12-
{gleam_http, "1.0.0"}
12+
{gleam_http, {git, "https://github.com/gleam-lang/gleam_http", {branch, "main"}}}
1313
]}.

rebar.lock

-8
This file was deleted.

src/gleam/httpc.gleam

+48-35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import gleam/dynamic.{Dynamic}
2-
import gleam/http.{Method}
2+
import gleam/http.{Method, Request, Response}
3+
import gleam/bit_string
4+
import gleam/result
35
import gleam/list
6+
import gleam/uri
47

58
external type Charlist
69

@@ -22,11 +25,15 @@ type ErlOption {
2225

2326
external fn erl_request(
2427
Method,
25-
tuple(Charlist, List(tuple(Charlist, Charlist)), Charlist, String),
28+
tuple(Charlist, List(tuple(Charlist, Charlist)), Charlist, BitString),
2629
List(ErlHttpOption),
2730
List(ErlOption),
2831
) -> Result(
29-
tuple(tuple(Charlist, Int, Charlist), List(tuple(Charlist, Charlist)), String),
32+
tuple(
33+
tuple(Charlist, Int, Charlist),
34+
List(tuple(Charlist, Charlist)),
35+
BitString,
36+
),
3037
Dynamic,
3138
) =
3239
"httpc" "request"
@@ -37,21 +44,15 @@ external fn erl_request_no_body(
3744
List(ErlHttpOption),
3845
List(ErlOption),
3946
) -> Result(
40-
tuple(tuple(Charlist, Int, Charlist), List(tuple(Charlist, Charlist)), String),
47+
tuple(
48+
tuple(Charlist, Int, Charlist),
49+
List(tuple(Charlist, Charlist)),
50+
BitString,
51+
),
4152
Dynamic,
4253
) =
4354
"httpc" "request"
4455

45-
pub type RequestBody {
46-
StringBody(content_type: String, body: String)
47-
BitBody(content_type: String, body: BitString)
48-
NoBody
49-
}
50-
51-
pub type Response {
52-
Response(status: Int, headers: List(tuple(String, String)), body: String)
53-
}
54-
5556
fn charlist_header(header: tuple(String, String)) -> tuple(Charlist, Charlist) {
5657
let tuple(k, v) = header
5758
tuple(binary_to_list(k), binary_to_list(v))
@@ -62,35 +63,47 @@ fn string_header(header: tuple(Charlist, Charlist)) -> tuple(String, String) {
6263
tuple(list_to_binary(k), list_to_binary(v))
6364
}
6465

66+
// TODO: test
6567
// TODO: refine error type
66-
pub fn request(
67-
method method: Method,
68-
url url: String,
69-
headers headers: List(tuple(String, String)),
70-
body body: RequestBody,
71-
) -> Result(Response, Dynamic) {
72-
let erl_url = binary_to_list(url)
73-
let erl_headers = list.map(headers, charlist_header)
68+
pub fn send_bits(
69+
req: Request(BitString),
70+
) -> Result(Response(BitString), Dynamic) {
71+
let erl_url = req
72+
|> http.req_to_uri
73+
|> uri.to_string
74+
|> binary_to_list
75+
let erl_headers = list.map(req.headers, charlist_header)
7476
let erl_http_options = []
7577
let erl_options = [BodyFormat(Binary)]
7678

77-
let response = case method, body {
78-
http.Options, _ | http.Head, _ | http.Get, _ | _, NoBody -> {
79-
let request = tuple(erl_url, erl_headers)
80-
erl_request_no_body(method, request, erl_http_options, erl_options)
79+
try response = case req.method {
80+
http.Options | http.Head | http.Get -> {
81+
let erl_req = tuple(erl_url, erl_headers)
82+
erl_request_no_body(req.method, erl_req, erl_http_options, erl_options)
8183
}
82-
_, StringBody(content_type: content_type, body: body) -> {
83-
let erl_content_type = binary_to_list(content_type)
84-
let request = tuple(erl_url, erl_headers, erl_content_type, body)
85-
erl_request(method, request, erl_http_options, erl_options)
84+
_ -> {
85+
let erl_content_type = req
86+
|> http.get_req_header("content-type")
87+
|> result.unwrap("application/octet-stream")
88+
|> binary_to_list
89+
let erl_req = tuple(erl_url, erl_headers, erl_content_type, req.body)
90+
erl_request(req.method, erl_req, erl_http_options, erl_options)
8691
}
8792
}
8893

89-
case response {
90-
Error(error) -> Error(error)
94+
let tuple(tuple(_version, status, _status), headers, resp_body) = response
95+
Ok(Response(status, list.map(headers, string_header), resp_body))
96+
}
9197

92-
Ok(
93-
tuple(tuple(_http_version, status, _status), headers, resp_body),
94-
) -> Ok(Response(status, list.map(headers, string_header), resp_body))
98+
// TODO: test
99+
// TODO: refine error type
100+
pub fn send(req: Request(String)) -> Result(Response(String), Dynamic) {
101+
try resp = req
102+
|> http.map_req_body(bit_string.from_string)
103+
|> send_bits
104+
105+
case bit_string.to_string(resp.body) {
106+
Ok(body) -> Ok(http.set_resp_body(resp, body))
107+
Error(_) -> Error(dynamic.from("Response body was not valid UTF-8"))
95108
}
96109
}

test/gleam_httpc_test.gleam

+70-55
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,85 @@
1-
import gleam/httpc.{NoBody, StringBody}
1+
import gleam/httpc
22
import gleam/http.{Get, Head, Options}
33
import gleam/list
44
import gleam/should
55

66
pub fn request_test() {
7-
let Ok(
8-
response,
9-
) = httpc.request(
10-
method: Get,
11-
url: "https://test-api.service.hmrc.gov.uk/hello/world",
12-
headers: [tuple("accept", "application/vnd.hmrc.1.0+json")],
13-
body: NoBody,
14-
)
15-
let httpc.Response(status, headers, body) = response
16-
should.equal(status, 200)
17-
should.equal(list.key_find(headers, "content-type"), Ok("application/json"))
18-
should.equal(body, "{\"message\":\"Hello World\"}")
7+
assert Ok(
8+
req,
9+
) = http.request(Get, "https://test-api.service.hmrc.gov.uk/hello/world")
10+
assert Ok(
11+
resp,
12+
) = req
13+
|> http.prepend_req_header("accept", "application/vnd.hmrc.1.0+json")
14+
|> httpc.send
15+
16+
resp.status
17+
|> should.equal(200)
18+
19+
resp
20+
|> http.get_resp_header("content-type")
21+
|> should.equal(Ok("application/json"))
22+
23+
resp.body
24+
|> should.equal("{\"message\":\"Hello World\"}")
1925
}
2026

2127
pub fn get_request_discards_body_test() {
22-
let Ok(
23-
response,
24-
) = httpc.request(
25-
method: Get,
26-
url: "https://test-api.service.hmrc.gov.uk/hello/world",
27-
headers: [tuple("accept", "application/vnd.hmrc.1.0+json")],
28-
body: StringBody(content_type: "application/json", body: "{}"),
29-
)
30-
let httpc.Response(status, headers, body) = response
31-
should.equal(status, 200)
32-
should.equal(list.key_find(headers, "content-type"), Ok("application/json"))
33-
should.equal(body, "{\"message\":\"Hello World\"}")
28+
assert Ok(
29+
req,
30+
) = http.request(Get, "https://test-api.service.hmrc.gov.uk/hello/world")
31+
assert Ok(
32+
resp,
33+
) = req
34+
|> http.prepend_req_header("accept", "application/vnd.hmrc.1.0+json")
35+
|> http.prepend_req_header("content-type", "application-json")
36+
|> httpc.send
37+
38+
resp.status
39+
|> should.equal(200)
40+
41+
resp
42+
|> http.get_resp_header("content-type")
43+
|> should.equal(Ok("application/json"))
44+
45+
resp.body
46+
|> should.equal("{\"message\":\"Hello World\"}")
3447
}
3548

3649
pub fn head_request_discards_body_test() {
37-
let Ok(
38-
response,
39-
) = httpc.request(
40-
method: Head,
41-
url: "https://postman-echo.com/get",
42-
headers: [],
43-
body: StringBody(content_type: "application/json", body: "{}"),
44-
)
45-
let httpc.Response(status, headers, body) = response
46-
should.equal(status, 200)
47-
should.equal(
48-
list.key_find(headers, "content-type"),
49-
Ok("application/json; charset=utf-8"),
50-
)
51-
should.equal(body, "")
50+
assert Ok(req) = http.request(Head, "https://postman-echo.com/get")
51+
assert Ok(
52+
resp,
53+
) = req
54+
|> http.set_req_body("This gets dropped")
55+
|> httpc.send
56+
57+
resp.status
58+
|> should.equal(200)
59+
60+
resp
61+
|> http.get_resp_header("content-type")
62+
|> should.equal(Ok("application/json; charset=utf-8"))
63+
64+
resp.body
65+
|> should.equal("")
5266
}
5367

5468
pub fn options_request_discards_body_test() {
55-
let Ok(
56-
response,
57-
) = httpc.request(
58-
method: Options,
59-
url: "https://postman-echo.com/get",
60-
headers: [],
61-
body: StringBody(content_type: "application/json", body: "{}"),
62-
)
63-
let httpc.Response(status, headers, body) = response
64-
should.equal(status, 200)
65-
should.equal(
66-
list.key_find(headers, "content-type"),
67-
Ok("text/html; charset=utf-8"),
68-
)
69-
should.equal(body, "GET,HEAD,PUT,POST,DELETE,PATCH")
69+
assert Ok(req) = http.request(Options, "https://postman-echo.com/get")
70+
assert Ok(
71+
resp,
72+
) = req
73+
|> http.set_req_body("This gets dropped")
74+
|> httpc.send
75+
76+
resp.status
77+
|> should.equal(200)
78+
79+
resp
80+
|> http.get_resp_header("content-type")
81+
|> should.equal(Ok("text/html; charset=utf-8"))
82+
83+
resp.body
84+
|> should.equal("GET,HEAD,PUT,POST,DELETE,PATCH")
7085
}

0 commit comments

Comments
 (0)