๐Ÿ‘ฉโ€๐Ÿš’ Firefighters API written in Gleam!
lustre gleam

:safety_vest: add email decoder

kacaii.dev 0100817f 52d379dd

verified
+52 -12
+1 -1
client/src/client/page/signup.gleam
··· 115 115 116 116 let effect = 117 117 rsvp.expect_json(user.decoder(), ServerSentResponse) 118 - |> rsvp.post("/api/signup", body, _) 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 - Error(_) -> wisp.unprocessable_content() 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 - Error(_) -> wisp.unprocessable_content() 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 + const test_email = "test@email.com" 13 + 12 14 pub fn signup_test() -> Nil { 13 15 use ctx <- server_test.with_context() 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 - email: wisp.random_string(12), 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 - simulate.browser_request(http.Post, url) 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 + pub fn signup_with_invalid_email_test() -> Nil { 51 + use ctx <- server_test.with_context() 52 + 53 + let body = 54 + signup_contract.RequestBody( 55 + name: wisp.random_string(12), 56 + role: role.None, 57 + email: "invalid_email", 58 + phone: wisp.random_string(12), 59 + password: wisp.random_string(12), 60 + is_active: False, 61 + ) 62 + |> signup_contract.request_to_json() 63 + 64 + let req = 65 + simulate.browser_request(http.Post, signup_contract.url) 66 + |> simulate.json_body(body) 67 + 68 + let resp = 69 + server_test.with_authorization(next: req, ctx:) 70 + |> router.handle_request(ctx) 71 + 72 + assert resp.status == 400 73 + } 74 + 49 75 pub fn signup_with_email_conflict_test() -> Nil { 50 76 use ctx <- server_test.with_context() 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 - simulate.browser_request(http.Post, url) 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 - 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 - email: wisp.random_string(12), 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 - simulate.browser_request(http.Post, url) 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 + import shared/user 5 + 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 - 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 + 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 + import gleam/result 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 + 51 + pub fn email_decoder() -> decode.Decoder(String) { 52 + use str <- decode.then(decode.string) 53 + let is_valid = 54 + !string.is_empty(str) && result.is_ok(string.split_once(str, "@")) 55 + 56 + case is_valid { 57 + True -> decode.success(str) 58 + False -> decode.failure("", "email") 59 + } 60 + }