tangled
alpha
login
or
join now
kacaii.dev
/
sigo
0
fork
atom
๐ฉโ๐ Firefighters API written in Gleam!
lustre
gleam
0
fork
atom
overview
issues
pulls
pipelines
:safety_vest: add email decoder
kacaii.dev
1 week ago
0100817f
52d379dd
verified
This commit was signed with the committer's
known signature
.
kacaii.dev
SSH Key Fingerprint:
SHA256:n9v7QGNWHCUv1x/483hCtPUvTsVabU5PzC5CSJMUNtI=
+52
-12
6 changed files
expand all
collapse all
unified
split
client
src
client
page
signup.gleam
server
src
server
router
login.gleam
signup.gleam
test
user_test.gleam
shared
src
shared
contract
signup.gleam
user.gleam
+1
-1
client/src/client/page/signup.gleam
···
115
115
116
116
let effect =
117
117
rsvp.expect_json(user.decoder(), ServerSentResponse)
118
118
-
|> rsvp.post("/api/signup", body, _)
118
118
+
|> rsvp.post(contract.url, body, _)
119
119
120
120
#(Model(..model, loading: True), effect)
121
121
}
+1
-1
server/src/server/router/login.gleam
···
25
25
26
26
case decode.run(body, contract.request_decoder()) {
27
27
Ok(body) -> handle_body(req, ctx, body)
28
28
-
Error(_) -> wisp.unprocessable_content()
28
28
+
Error(_) -> wisp.bad_request("Invalid format")
29
29
}
30
30
}
31
31
+1
-1
server/src/server/router/signup.gleam
···
18
18
19
19
case decode.run(body, contract.request_decoder()) {
20
20
Ok(body) -> handle_body(body, ctx)
21
21
-
Error(_) -> wisp.unprocessable_content()
21
21
+
Error(_) -> wisp.bad_request("Invalid format")
22
22
}
23
23
}
24
24
+32
-8
server/test/user_test.gleam
···
9
9
import wisp
10
10
import wisp/simulate
11
11
12
12
+
const test_email = "test@email.com"
13
13
+
12
14
pub fn signup_test() -> Nil {
13
15
use ctx <- server_test.with_context()
14
14
-
let url = "/api/signup"
15
16
16
17
let body =
17
18
signup_contract.RequestBody(
18
19
name: wisp.random_string(12),
19
20
role: role.None,
20
20
-
email: wisp.random_string(12),
21
21
+
email: test_email,
21
22
phone: wisp.random_string(12),
22
23
password: wisp.random_string(12),
23
24
is_active: False,
···
25
26
|> signup_contract.request_to_json()
26
27
27
28
let req =
28
28
-
simulate.browser_request(http.Post, url)
29
29
+
simulate.browser_request(http.Post, signup_contract.url)
29
30
|> simulate.json_body(body)
30
31
31
32
// not authenticated
···
46
47
}
47
48
}
48
49
50
50
+
pub fn signup_with_invalid_email_test() -> Nil {
51
51
+
use ctx <- server_test.with_context()
52
52
+
53
53
+
let body =
54
54
+
signup_contract.RequestBody(
55
55
+
name: wisp.random_string(12),
56
56
+
role: role.None,
57
57
+
email: "invalid_email",
58
58
+
phone: wisp.random_string(12),
59
59
+
password: wisp.random_string(12),
60
60
+
is_active: False,
61
61
+
)
62
62
+
|> signup_contract.request_to_json()
63
63
+
64
64
+
let req =
65
65
+
simulate.browser_request(http.Post, signup_contract.url)
66
66
+
|> simulate.json_body(body)
67
67
+
68
68
+
let resp =
69
69
+
server_test.with_authorization(next: req, ctx:)
70
70
+
|> router.handle_request(ctx)
71
71
+
72
72
+
assert resp.status == 400
73
73
+
}
74
74
+
49
75
pub fn signup_with_email_conflict_test() -> Nil {
50
76
use ctx <- server_test.with_context()
51
51
-
let url = "/api/signup"
52
77
53
78
let body =
54
79
signup_contract.RequestBody(
···
62
87
|> signup_contract.request_to_json()
63
88
64
89
let req =
65
65
-
simulate.browser_request(http.Post, url)
90
90
+
simulate.browser_request(http.Post, signup_contract.url)
66
91
|> simulate.json_body(body)
67
92
68
93
let resp =
···
74
99
75
100
pub fn signup_with_phone_conflict_test() -> Nil {
76
101
use ctx <- server_test.with_context()
77
77
-
let url = "/api/signup"
78
102
79
103
let body =
80
104
signup_contract.RequestBody(
81
105
name: wisp.random_string(12),
82
106
role: role.None,
83
83
-
email: wisp.random_string(12),
107
107
+
email: test_email,
84
108
phone: seed.phone,
85
109
password: wisp.random_string(12),
86
110
is_active: False,
···
88
112
|> signup_contract.request_to_json()
89
113
90
114
let req =
91
91
-
simulate.browser_request(http.Post, url)
115
115
+
simulate.browser_request(http.Post, signup_contract.url)
92
116
|> simulate.json_body(body)
93
117
94
118
let resp =
+4
-1
shared/src/shared/contract/signup.gleam
···
1
1
import gleam/dynamic/decode
2
2
import gleam/json
3
3
import shared/role
4
4
+
import shared/user
5
5
+
6
6
+
pub const url = "/api/signup"
4
7
5
8
pub type RequestBody {
6
9
RequestBody(
···
27
30
pub fn request_decoder() -> decode.Decoder(RequestBody) {
28
31
use name <- decode.field("name", decode.string)
29
32
use role <- decode.field("role", role.decoder())
30
30
-
use email <- decode.field("email", decode.string)
31
33
use phone <- decode.field("phone", decode.string)
32
34
use password <- decode.field("password", decode.string)
33
35
use is_active <- decode.field("is_active", decode.bool)
36
36
+
use email <- decode.field("email", user.email_decoder())
34
37
35
38
RequestBody(name:, role:, email:, phone:, password:, is_active:)
36
39
|> decode.success
+13
shared/src/shared/user.gleam
···
1
1
import gleam/dynamic/decode
2
2
import gleam/json
3
3
+
import gleam/result
4
4
+
import gleam/string
3
5
import shared/role
4
6
import youid/uuid
5
7
···
45
47
fn uuid_to_json(uuid: uuid.Uuid) {
46
48
uuid.to_string(uuid) |> json.string()
47
49
}
50
50
+
51
51
+
pub fn email_decoder() -> decode.Decoder(String) {
52
52
+
use str <- decode.then(decode.string)
53
53
+
let is_valid =
54
54
+
!string.is_empty(str) && result.is_ok(string.split_once(str, "@"))
55
55
+
56
56
+
case is_valid {
57
57
+
True -> decode.success(str)
58
58
+
False -> decode.failure("", "email")
59
59
+
}
60
60
+
}