-
Notifications
You must be signed in to change notification settings - Fork 0
/
service.gleam
102 lines (94 loc) · 2.95 KB
/
service.gleam
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//// Service module that's mostly the same as the one in gleam/http but
//// instead of taking gleam/http request and response it takes espresso versions.
////
//// Forked from https://github.com/gleam-lang/http/blob/v3.2.0/src/gleam/http/service.gleam
import espresso/request.{Request}
import espresso/response.{Response}
import gleam/http.{Delete, Patch, Post, Put}
import gleam/list
import gleam/result
/// Services are functions that take a request and return a response.
/// They are the core building block of web applications.
pub type Service(in, assigns, session, out) =
fn(Request(in, assigns, session)) -> Response(out)
/// Middleware are functions that take a before req/res and return a
/// new req/res. They are used to transform requests and responses.
pub type Middleware(
before_req,
assigns,
session,
before_resp,
after_req,
after_resp,
) =
fn(Service(before_req, assigns, session, before_resp)) ->
Service(after_req, assigns, session, after_resp)
/// A middleware that transforms the response body returned by the service using
/// a given function.
///
pub fn map_response_body(
service: Service(req, assigns, session, resp),
with mapper: fn(resp) -> b,
) -> Service(req, assigns, session, b) {
fn(req) {
req
|> service
|> response.map(mapper)
}
}
/// A middleware that prepends a header to the request.
///
pub fn prepend_response_header(
service: Service(req, assigns, session, resp),
key: String,
value: String,
) -> Service(req, assigns, session, resp) {
fn(req) {
req
|> service
|> response.prepend_header(key, value)
}
}
fn ensure_post(req: Request(body, assigns, session)) {
case req.method {
Post -> Ok(req)
_ -> Error(Nil)
}
}
fn get_override_method(
request: Request(body, assigns, session),
) -> Result(http.Method, Nil) {
use query_params <- result.then(request.get_query(request))
use method <- result.then(list.key_find(query_params, "_method"))
use method <- result.then(http.parse_method(method))
case method {
Put | Patch | Delete -> Ok(method)
_ -> Error(Nil)
}
}
/// A middleware that overrides an incoming POST request with a method given in
/// the request's `_method` query paramerter. This is useful as web browsers
/// typically only support GET and POST requests, but our application may
/// expect other HTTP methods that are more semantically correct.
///
/// The methods PUT, PATCH, and DELETE are accepted for overriding, all others
/// are ignored.
///
/// The `_method` query paramerter can be specified in a HTML form like so:
///
/// <form method="POST" action="/item/1?_method=DELETE">
/// <button type="submit">Delete item</button>
/// </form>
///
pub fn method_override(
service: Service(req, assigns, session, resp),
) -> Service(req, assigns, session, resp) {
fn(request) {
request
|> ensure_post
|> result.then(get_override_method)
|> result.map(request.set_method(request, _))
|> result.unwrap(request)
|> service
}
}