wip: currently rewriting the project as a full stack application tangled.org/kacaii.dev/sigo
gleam

:sparkles: add routes to update and delete an occurrence `resolved_at` field

+318 -159
+25 -24
README.md
··· 8 8 9 9 ## Routes 10 10 11 - | Route | Description | Method | 12 - | ------------------------------ | ----------------------------------------------------------- | ----------- | 13 - | /admin/setup | Create the first admin user | POST (JSON) | 14 - | /admin/signup | Register a new user account | POST (Form) | 15 - | /admin/users | List all registred users | GET | 16 - | /admin/users/{{id}} | Delete an user from the DataBase | DELETE | 17 - | /admin/users/{{id}} | Update the user data as an admin | PUT (JSON) | 18 - | /admin/users/{{id}}/status | Update the status of a user account | PUT (JSON) | 19 - | /admin/teams | Register a new brigade, with a leader and all their members | POST (Form) | 20 - | /admin/teams | Query all registered brigades | GET | 21 - | /admin/teams/{{id}}/status | Update the status of a brigade | PUT (JSON) | 22 - | /admin/teams/{{id}} | Remove a brigade | DELETE | 23 - | /user/login | Login with your user account | POST (Form) | 24 - | /user/profile | Retrieve data about the authenticated user | GET | 25 - | /user/profile | Update your profile | PUT(JSON) | 26 - | /user/roles | Get a list of all available roles | GET | 27 - | /user/{{id}}/occurrences | Find all occurrences applied by this user | GET | 28 - | /user/{{id}}/crew_members | List fellow brigade members of this user | GET | 29 - | /user/notification_preferences | Fetch authenticated user notification preferences | GET | 30 - | /user/notification_preferences | Update authenticated user notification preferences | PUT (JSON) | 31 - | /user/password | Update authenticated user password | PUT (JSON) | 32 - | /brigade/{{id}}/members | List brigade members | GET | 33 - | /occurrence/new | Register new occurrence | POST (Form) | 34 - | /dashboard/stats | Fetch stats for the dashboard page | GET | 11 + | Route | Description | Method | 12 + | ------------------------------ | ----------------------------------------------------------- | ------------- | 13 + | /admin/setup | Create the first admin user | POST (JSON) | 14 + | /admin/signup | Register a new user account | POST (Form) | 15 + | /admin/users | List all registred users | GET | 16 + | /admin/users/{{id}} | Delete an user from the DataBase | DELETE | 17 + | /admin/users/{{id}} | Update the user data as an admin | PUT (JSON) | 18 + | /admin/users/{{id}}/status | Update the status of a user account | PUT (JSON) | 19 + | /admin/teams | Register a new brigade, with a leader and all their members | POST (Form) | 20 + | /admin/teams | Query all registered brigades | GET | 21 + | /admin/teams/{{id}}/status | Update the status of a brigade | PUT (JSON) | 22 + | /admin/teams/{{id}} | Remove a brigade | DELETE | 23 + | /user/login | Login with your user account | POST (Form) | 24 + | /user/profile | Retrieve data about the authenticated user | GET | 25 + | /user/profile | Update your profile | PUT(JSON) | 26 + | /user/roles | Get a list of all available roles | GET | 27 + | /user/{{id}}/occurrences | Find all occurrences applied by this user | GET | 28 + | /user/{{id}}/crew_members | List fellow brigade members of this user | GET | 29 + | /user/notification_preferences | Fetch authenticated user notification preferences | GET | 30 + | /user/notification_preferences | Update authenticated user notification preferences | PUT (JSON) | 31 + | /user/password | Update authenticated user password | PUT (JSON) | 32 + | /brigade/{{id}}/members | List brigade members | GET | 33 + | /dashboard/stats | Fetch stats for the dashboard page | GET | 34 + | /occurrence/new | Register a new occurrence | POST (Form) | 35 + | /occurrence/resolve/{{id}} | Update an occurrence `resolved_at` field | POST / DELETE | 35 36 36 37 ## Entity RelationShip Diagram 37 38
+3 -3
src/app.gleam
··· 9 9 //// 1. A PostgreSQL database connection pool using Pog 10 10 //// 2. An HTTP server using Mist (with Wisp handling the web layer) 11 11 12 - import app/router 12 + import app/http_router 13 13 import app/routes/admin/sql as admin_sql 14 14 import app/supervision_tree 15 15 import app/web ··· 58 58 secret_key_base:, 59 59 ) 60 60 61 - let wisp_handler = router.handle_request(_, ctx) 61 + let wisp_handler = http_router.handle_request(_, ctx) 62 62 let ws_handler = socket.handle_request(_, ctx) 63 63 64 64 // Start all essential processes under a supervision tree ··· 120 120 simulate.browser_request(http.Post, "/admin/setup") 121 121 |> simulate.json_body(json.object([#("key", json.string("admin"))])) 122 122 123 - router.handle_request(setup_admin_req, ctx) 123 + http_router.handle_request(setup_admin_req, ctx) 124 124 io.println(" Administrador cadastrado com sucesso!") 125 125 } 126 126 }
+4 -1
src/app/router.gleam src/app/http_router.gleam
··· 20 20 import app/routes/occurrence/delete_occurrence 21 21 import app/routes/occurrence/get_ocurrences_by_applicant 22 22 import app/routes/occurrence/register_new_occurrence 23 + import app/routes/occurrence/update_occurrence_status 23 24 import app/routes/role/get_role_list 24 25 import app/routes/user/delete_user 25 26 import app/routes/user/get_all_user_profiles ··· 107 108 http.Delete, ["occurrence", id] -> 108 109 delete_occurrence.handle_request(request:, ctx:, id:) 109 110 110 - http.Put, ["occurrence", "resolve", id] -> todo as "handle request" 111 + http.Post, ["occurrence", "resolved_at", id] 112 + | http.Delete, ["occurrence", "resolved_at", id] 113 + -> update_occurrence_status.handle_request(request:, ctx:, id:) 111 114 112 115 // 󰢫 Brigade routes ------------------------------------------------------- 113 116 http.Get, ["brigade", id, "members"] ->
-85
src/app/routes/occurrence/resolve_occurrence.gleam
··· 1 - import app/routes/occurrence 2 - import app/routes/occurrence/sql 3 - import app/web 4 - import app/web/context.{type Context} 5 - import app/web/socket/message as msg 6 - import gleam/dynamic/decode 7 - import gleam/http 8 - import gleam/json 9 - import gleam/list 10 - import gleam/result.{try} 11 - import gleam/time/timestamp 12 - import pog 13 - import wisp 14 - import youid/uuid 15 - 16 - type ResolveOccurrenceError { 17 - InvalidUuid(String) 18 - OccurrenceNotFound(uuid.Uuid) 19 - DataBaseError(pog.QueryError) 20 - } 21 - 22 - pub fn handle_request( 23 - request req: wisp.Request, 24 - ctx ctx: Context, 25 - id occurrence_id: String, 26 - ) -> wisp.Response { 27 - use <- wisp.require_method(req, http.Put) 28 - 29 - case try_resolve_occurrence(ctx, occurrence_id) { 30 - Error(err) -> handle_error(err) 31 - Ok(data) -> handle_body(data) 32 - } 33 - } 34 - 35 - fn handle_body(data: json.Json) -> wisp.Response { 36 - todo 37 - } 38 - 39 - //  Zig: const gamer = try bagulho() 40 - // 41 - //  gleam: let gamer = result.try(bagulho()) 42 - //  gleam: let gamer = try(bagulho()) 43 - 44 - fn try_resolve_occurrence(ctx: Context, occ_id: String) { 45 - let update_result = { 46 - use target <- try( 47 - uuid.from_string(occ_id) 48 - |> result.replace_error(InvalidUuid(occ_id)), 49 - ) 50 - use returned <- try( 51 - sql.resolve_occurrence(ctx.db, target) 52 - |> result.map_error(DataBaseError), 53 - ) 54 - 55 - use row <- result.map( 56 - list.first(returned.rows) 57 - |> result.replace_error(OccurrenceNotFound(target)), 58 - ) 59 - 60 - let timestamp_json = 61 - json.nullable(row.resolved_at, fn(time) { 62 - timestamp.to_unix_seconds(time) |> json.float 63 - }) 64 - 65 - occurrence.broadcast( 66 - ctx:, 67 - registry:, 68 - occ_id: row.id, 69 - message: msg.OccurrenceResolved(occ_id: row.id, when: row.resolved_at), 70 - ) 71 - 72 - json.object([ 73 - #("id", json.string(uuid.to_string(row.id))), 74 - #("resolved_at", timestamp_json), 75 - ]) 76 - } 77 - } 78 - 79 - fn handle_error(err: ResolveOccurrenceError) -> wisp.Response { 80 - case err { 81 - DataBaseError(err) -> web.handle_database_error(err) 82 - InvalidUuid(_) -> todo 83 - OccurrenceNotFound(_) -> todo 84 - } 85 - }
+61 -6
src/app/routes/occurrence/sql.gleam
··· 386 386 |> pog.execute(db) 387 387 } 388 388 389 + /// A row you get from running the `reopen_occurrence` query 390 + /// defined in `./src/app/routes/occurrence/sql/reopen_occurrence.sql`. 391 + /// 392 + /// > 🐿️ This type definition was generated automatically using v4.5.0 of the 393 + /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 394 + /// 395 + pub type ReopenOccurrenceRow { 396 + ReopenOccurrenceRow( 397 + id: Uuid, 398 + resolved_at: Option(Timestamp), 399 + updated_at: Timestamp, 400 + ) 401 + } 402 + 403 + /// 󰚰 Mark a occurrence as unresolved 404 + /// 405 + /// > 🐿️ This function was generated automatically using v4.5.0 of 406 + /// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 407 + /// 408 + pub fn reopen_occurrence( 409 + db: pog.Connection, 410 + arg_1: Uuid, 411 + ) -> Result(pog.Returned(ReopenOccurrenceRow), pog.QueryError) { 412 + let decoder = { 413 + use id <- decode.field(0, uuid_decoder()) 414 + use resolved_at <- decode.field(1, decode.optional(pog.timestamp_decoder())) 415 + use updated_at <- decode.field(2, pog.timestamp_decoder()) 416 + decode.success(ReopenOccurrenceRow(id:, resolved_at:, updated_at:)) 417 + } 418 + 419 + "-- 󰚰 Mark a occurrence as unresolved 420 + UPDATE public.occurrence 421 + SET 422 + resolved_at = NULL, 423 + updated_at = CURRENT_TIMESTAMP 424 + WHERE id = $1 425 + RETURNING 426 + id, 427 + resolved_at, 428 + updated_at; 429 + " 430 + |> pog.query 431 + |> pog.parameter(pog.text(uuid.to_string(arg_1))) 432 + |> pog.returning(decoder) 433 + |> pog.execute(db) 434 + } 435 + 389 436 /// A row you get from running the `replace_occurrence_brigades` query 390 437 /// defined in `./src/app/routes/occurrence/sql/replace_occurrence_brigades.sql`. 391 438 /// ··· 431 478 /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 432 479 /// 433 480 pub type ResolveOccurrenceRow { 434 - ResolveOccurrenceRow(id: Uuid, resolved_at: Option(Timestamp)) 481 + ResolveOccurrenceRow( 482 + id: Uuid, 483 + resolved_at: Option(Timestamp), 484 + updated_at: Timestamp, 485 + ) 435 486 } 436 487 437 - /// 󰚰 Resolve a occurrence 488 + /// 󰚰 Mark a occurrence as resolved 438 489 /// 439 490 /// > 🐿️ This function was generated automatically using v4.5.0 of 440 491 /// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). ··· 446 497 let decoder = { 447 498 use id <- decode.field(0, uuid_decoder()) 448 499 use resolved_at <- decode.field(1, decode.optional(pog.timestamp_decoder())) 449 - decode.success(ResolveOccurrenceRow(id:, resolved_at:)) 500 + use updated_at <- decode.field(2, pog.timestamp_decoder()) 501 + decode.success(ResolveOccurrenceRow(id:, resolved_at:, updated_at:)) 450 502 } 451 503 452 - "-- 󰚰 Resolve a occurrence 504 + "-- 󰚰 Mark a occurrence as resolved 453 505 UPDATE public.occurrence 454 - SET resolved_at = CURRENT_TIMESTAMP 506 + SET 507 + resolved_at = CURRENT_TIMESTAMP, 508 + updated_at = CURRENT_TIMESTAMP 455 509 WHERE id = $1 456 510 RETURNING 457 511 id, 458 - resolved_at; 512 + resolved_at, 513 + updated_at; 459 514 " 460 515 |> pog.query 461 516 |> pog.parameter(pog.text(uuid.to_string(arg_1)))
+10
src/app/routes/occurrence/sql/reopen_occurrence.sql
··· 1 + -- 󰚰 Mark a occurrence as unresolved 2 + UPDATE public.occurrence 3 + SET 4 + resolved_at = NULL, 5 + updated_at = CURRENT_TIMESTAMP 6 + WHERE id = $1 7 + RETURNING 8 + id, 9 + resolved_at, 10 + updated_at;
+6 -3
src/app/routes/occurrence/sql/resolve_occurrence.sql
··· 1 - -- 󰚰 Resolve a occurrence 1 + -- 󰚰 Mark a occurrence as resolved 2 2 UPDATE public.occurrence 3 - SET resolved_at = CURRENT_TIMESTAMP 3 + SET 4 + resolved_at = CURRENT_TIMESTAMP, 5 + updated_at = CURRENT_TIMESTAMP 4 6 WHERE id = $1 5 7 RETURNING 6 8 id, 7 - resolved_at; 9 + resolved_at, 10 + updated_at;
+156
src/app/routes/occurrence/update_occurrence_status.gleam
··· 1 + import app/routes/occurrence 2 + import app/routes/occurrence/sql 3 + import app/routes/user 4 + import app/web 5 + import app/web/context.{type Context} 6 + import app/web/socket/message as msg 7 + import gleam/http 8 + import gleam/json 9 + import gleam/list 10 + import gleam/result.{try} 11 + import gleam/time/timestamp 12 + import group_registry 13 + import pog 14 + import wisp 15 + import youid/uuid 16 + 17 + /// Resolving a occurence can fail 18 + type ResolveOccurrenceError { 19 + /// Occurrence has invalid Uuid format 20 + InvalidUuid(String) 21 + /// Occurrence was not found in the DataBase 22 + OccurrenceNotFound(uuid.Uuid) 23 + /// An error occurred whe naccessing the DataBase 24 + DataBaseError(pog.QueryError) 25 + /// Errors related to authentication / authorization 26 + AccessControl(user.AuthenticationError) 27 + } 28 + 29 + /// 󰚰 Updates the `resolved_at` field of a occurrence 30 + /// 31 + /// ```jsonc 32 + /// { 33 + /// "id": "a32fb57f-b547-434d-a5d6-2a2c96cddb20", 34 + /// "resolved_at": 1762889998.0, // or null 35 + /// "updated_at": 1762889998.0 36 + /// } 37 + /// ```` 38 + pub fn handle_request( 39 + request req: wisp.Request, 40 + ctx ctx: Context, 41 + id occurrence_id: String, 42 + ) -> wisp.Response { 43 + case req.method { 44 + http.Post -> 45 + case try_resolve_occurrence(req, ctx, occurrence_id) { 46 + Error(err) -> handle_error(err) 47 + Ok(body) -> wisp.json_response(body, 200) 48 + } 49 + http.Delete -> 50 + case try_reopen_occurrence(req, ctx, occurrence_id) { 51 + Error(err) -> handle_error(err) 52 + Ok(body) -> wisp.json_response(body, 200) 53 + } 54 + _ -> wisp.method_not_allowed([http.Post, http.Delete]) 55 + } 56 + } 57 + 58 + fn try_resolve_occurrence( 59 + req: wisp.Request, 60 + ctx: Context, 61 + occ_id: String, 62 + ) -> Result(String, ResolveOccurrenceError) { 63 + use _ <- try( 64 + user.extract_uuid(request: req, cookie_name: user.uuid_cookie_name) 65 + |> result.map_error(AccessControl), 66 + ) 67 + 68 + use target <- try( 69 + uuid.from_string(occ_id) 70 + |> result.replace_error(InvalidUuid(occ_id)), 71 + ) 72 + 73 + use returned <- try( 74 + sql.resolve_occurrence(ctx.db, target) 75 + |> result.map_error(DataBaseError), 76 + ) 77 + 78 + use row <- result.map( 79 + list.first(returned.rows) 80 + |> result.replace_error(OccurrenceNotFound(target)), 81 + ) 82 + 83 + let timestamp_json = 84 + json.nullable(row.resolved_at, fn(time) { 85 + timestamp.to_unix_seconds(time) |> json.float 86 + }) 87 + 88 + //  Broadcast to all assigned users ---------------------------------------- 89 + let _ = 90 + occurrence.broadcast( 91 + ctx:, 92 + registry: group_registry.get_registry(ctx.registry_name), 93 + occurrence: row.id, 94 + message: msg.OccurrenceResolved(occ_id: row.id, when: row.resolved_at), 95 + ) 96 + 97 + // RESPONSE 98 + json.to_string( 99 + json.object([ 100 + #("id", json.string(uuid.to_string(row.id))), 101 + #("resolved_at", timestamp_json), 102 + #("updated_at", json.float(timestamp.to_unix_seconds(row.updated_at))), 103 + ]), 104 + ) 105 + } 106 + 107 + fn try_reopen_occurrence( 108 + req: wisp.Request, 109 + ctx: Context, 110 + occ_id: String, 111 + ) -> Result(String, ResolveOccurrenceError) { 112 + use _ <- try( 113 + user.extract_uuid(request: req, cookie_name: user.uuid_cookie_name) 114 + |> result.map_error(AccessControl), 115 + ) 116 + 117 + use target <- try( 118 + uuid.from_string(occ_id) 119 + |> result.replace_error(InvalidUuid(occ_id)), 120 + ) 121 + 122 + use returned <- try( 123 + sql.reopen_occurrence(ctx.db, target) 124 + |> result.map_error(DataBaseError), 125 + ) 126 + 127 + use row <- result.map( 128 + list.first(returned.rows) 129 + |> result.replace_error(OccurrenceNotFound(target)), 130 + ) 131 + 132 + let timestamp_json = 133 + json.nullable(row.resolved_at, fn(time) { 134 + timestamp.to_unix_seconds(time) |> json.float 135 + }) 136 + 137 + json.to_string( 138 + json.object([ 139 + #("id", json.string(uuid.to_string(row.id))), 140 + #("resolved_at", timestamp_json), 141 + #("updated_at", json.float(timestamp.to_unix_seconds(row.updated_at))), 142 + ]), 143 + ) 144 + } 145 + 146 + fn handle_error(err: ResolveOccurrenceError) -> wisp.Response { 147 + case err { 148 + AccessControl(err) -> user.handle_authentication_error(err) 149 + DataBaseError(err) -> web.handle_database_error(err) 150 + InvalidUuid(id) -> 151 + wisp.bad_request("Ocorrência possui Uuid inválido: " <> id) 152 + OccurrenceNotFound(occ_id) -> 153 + wisp.Text("Ocorrência não encontrada: " <> uuid.to_string(occ_id)) 154 + |> wisp.set_body(wisp.not_found(), _) 155 + } 156 + }
+17 -1
src/app/web/socket.gleam
··· 159 159 ]), 160 160 ) 161 161 } 162 - msg.OccurrenceResolved(occ_id:, when:) -> todo 162 + 163 + msg.OccurrenceResolved(occ_id:, when:) -> { 164 + send_envelope( 165 + state:, 166 + conn:, 167 + data_type: "occurrence_resolved", 168 + data: json.object([ 169 + #("id", json.string(uuid.to_string(occ_id))), 170 + #( 171 + "timestamp", 172 + json.nullable(when, fn(time) { 173 + timestamp.to_unix_seconds(time) |> json.float 174 + }), 175 + ), 176 + ]), 177 + ) 178 + } 163 179 } 164 180 } 165 181
+5 -5
test/admin_test.gleam
··· 1 - import app/router 1 + import app/http_router 2 2 import app/routes/role 3 3 import app/routes/user/sql as u_sql 4 4 import app_dev/sql as dev_sql ··· 40 40 ]), 41 41 ) 42 42 43 - let resp = router.handle_request(req, ctx) 43 + let resp = http_router.handle_request(req, ctx) 44 44 assert resp.status == 401 45 45 as "Endpoint only accessible for authenticated Admin users" 46 46 47 47 let req = app_test.with_authorization(req) 48 - let resp = router.handle_request(req, ctx) 48 + let resp = http_router.handle_request(req, ctx) 49 49 50 50 assert resp.status == 200 as "Response should be HTTP 200 OK" 51 51 ··· 112 112 simulate.request(http.Put, path) 113 113 |> simulate.json_body(json.object([#("status", json.bool(target_status))])) 114 114 115 - let resp = router.handle_request(req, ctx) 115 + let resp = http_router.handle_request(req, ctx) 116 116 117 117 assert resp.status == 401 as "Only accessible to Admin users" 118 118 119 119 let req = app_test.with_authorization(req) 120 - let resp = router.handle_request(req, ctx) 120 + let resp = http_router.handle_request(req, ctx) 121 121 122 122 assert resp.status == 200 as "Status should be 200" 123 123 let body = simulate.read_body(resp)
+2 -2
test/app_test.gleam
··· 1 1 import app 2 - import app/router 2 + import app/http_router 3 3 import app/web/context.{type Context, Context} 4 4 import gleam/erlang/process 5 5 import gleam/http ··· 44 44 simulate.browser_request(http.Post, "/user/login") 45 45 |> simulate.form_body([#("matricula", "000"), #("senha", "aluno")]) 46 46 47 - let login_resp = router.handle_request(login_req, ctx) 47 + let login_resp = http_router.handle_request(login_req, ctx) 48 48 49 49 // Continue the session after being logged in 50 50 simulate.session(req, login_req, login_resp)
+5 -5
test/brigade_test.gleam
··· 1 - import app/router 1 + import app/http_router 2 2 import app/routes/role 3 3 import app_dev/sql as dev_sql 4 4 import app_test ··· 40 40 ) 41 41 42 42 // REGULAR REQUEST 43 - let resp = router.handle_request(req, ctx) 43 + let resp = http_router.handle_request(req, ctx) 44 44 assert resp.status == 401 as "Endpoint restricted to Admin users" 45 45 46 46 // AS ADMIN 47 47 let with_auth = app_test.with_authorization(req) 48 48 49 49 // AUTHORIZED REQUEST 50 - let resp = router.handle_request(with_auth, ctx) 50 + let resp = http_router.handle_request(with_auth, ctx) 51 51 assert resp.status == 201 as "Response sould be HTTP 201 CREATED" 52 52 53 53 // READ BODY ----------------------------------------------------------------- ··· 112 112 let path = "/brigade/" <> uuid.to_string(dummy_brigade) <> "/members" 113 113 let req = simulate.browser_request(http.Get, path) 114 114 115 - let resp = router.handle_request(req, ctx) 115 + let resp = http_router.handle_request(req, ctx) 116 116 assert resp.status == 200 as "Response should be HTTP 200 OK" 117 117 118 118 let body = simulate.read_body(resp) ··· 176 176 177 177 // START --------------------------------------------------------------------- 178 178 let req = simulate.browser_request(http.Get, path) 179 - let resp = router.handle_request(req, ctx) 179 + let resp = http_router.handle_request(req, ctx) 180 180 181 181 assert resp.status == 200 as "Enpoint should be accessible" 182 182
+2 -2
test/dashboard_test.gleam
··· 1 - import app/router 1 + import app/http_router 2 2 import app_test 3 3 import gleam/dynamic/decode 4 4 import gleam/http ··· 9 9 let ctx = app_test.global_data() 10 10 11 11 let req = simulate.browser_request(http.Get, "/dashboard/stats") 12 - let resp = router.handle_request(req, ctx) 12 + let resp = http_router.handle_request(req, ctx) 13 13 assert resp.status == 200 as "Response status should be 200" 14 14 15 15 let body = simulate.read_body(resp)
+6 -6
test/occurence_test.gleam
··· 1 - import app/router 1 + import app/http_router 2 2 import app/routes/occurrence/category 3 3 import app/routes/occurrence/priority 4 4 import app/routes/occurrence/sql as o_sql ··· 58 58 ) 59 59 60 60 // RESPONSE ------------------------------------------------------------------ 61 - let resp = router.handle_request(req, ctx) 61 + let resp = http_router.handle_request(req, ctx) 62 62 assert resp.status != 422 as "Invalid request Payload" 63 63 assert resp.status == 401 64 64 as "Endpoint should only accessible to authenticated users" 65 65 66 66 let with_auth = app_test.with_authorization(next: req) 67 - let resp = router.handle_request(with_auth, ctx) 67 + let resp = http_router.handle_request(with_auth, ctx) 68 68 assert resp.status == 201 as "Status should be HTTP 201 Created" 69 69 70 70 // JSON PARSING -------------------------------------------------------------- ··· 156 156 let path = "/user/" <> uuid.to_string(dummy_applicant_id) <> "/occurrences" 157 157 let req = simulate.browser_request(http.Get, path) 158 158 159 - let resp = router.handle_request(req, ctx) 159 + let resp = http_router.handle_request(req, ctx) 160 160 let body = simulate.read_body(resp) 161 161 assert resp.status == 200 162 162 ··· 252 252 253 253 let path = "/occurrence/" <> uuid.to_string(dummy_occurrence) 254 254 let req = simulate.request(http.Delete, path) 255 - let resp = router.handle_request(req, ctx) 255 + let resp = http_router.handle_request(req, ctx) 256 256 257 257 assert resp.status == 401 as "Only accessible to Admins" 258 258 259 259 let with_auth = app_test.with_authorization(req) 260 - let resp = router.handle_request(with_auth, ctx) 260 + let resp = http_router.handle_request(with_auth, ctx) 261 261 262 262 assert resp.status == 200 as "Status should be HTTP 200 OK" 263 263
+2 -2
test/role_test.gleam
··· 1 - import app/router 1 + import app/http_router 2 2 import app/routes/role 3 3 import app_test 4 4 import gleam/dynamic/decode ··· 11 11 let ctx = app_test.global_data() 12 12 13 13 let req = simulate.browser_request(http.Get, "/user/roles") 14 - let resp = router.handle_request(req, ctx) 14 + let resp = http_router.handle_request(req, ctx) 15 15 16 16 assert resp.status == 200 as "Status code should be 200 OK" 17 17 let body = simulate.read_body(resp)
+14 -14
test/user_test.gleam
··· 1 - import app/router 1 + import app/http_router 2 2 import app/routes/role 3 3 import app/routes/user 4 4 import app_dev/sql as dev_sql ··· 23 23 simulate.browser_request(http.Post, "/user/login") 24 24 |> simulate.form_body([#("matricula", "000"), #("senha", "aluno")]) 25 25 26 - let resp = router.handle_request(req, ctx) 26 + let resp = http_router.handle_request(req, ctx) 27 27 assert resp.status == 200 as "Status should be 200" 28 28 29 29 let body = simulate.read_body(resp) ··· 68 68 #("cargo", role.to_string_pt_br(dummy.random_role())), 69 69 ]) 70 70 71 - let resp = router.handle_request(req, ctx) 71 + let resp = http_router.handle_request(req, ctx) 72 72 assert resp.status == 401 as "Endpoint access should be restricted" 73 73 74 74 //  AUTH ------------------------------------------------------------------- 75 75 let with_auth = app_test.with_authorization(next: req) 76 - let resp = router.handle_request(with_auth, ctx) 76 + let resp = http_router.handle_request(with_auth, ctx) 77 77 assert resp.status == 201 as "Response should be 201 Created" 78 78 79 79 let body = simulate.read_body(resp) ··· 107 107 ]) 108 108 109 109 let with_auth = app_test.with_authorization(next: req) 110 - let resp = router.handle_request(with_auth, ctx) 110 + let resp = http_router.handle_request(with_auth, ctx) 111 111 assert resp.status == 409 as "Registration should be unique" 112 112 } 113 113 ··· 127 127 ]) 128 128 129 129 let with_auth = app_test.with_authorization(next: req) 130 - let resp = router.handle_request(with_auth, ctx) 130 + let resp = http_router.handle_request(with_auth, ctx) 131 131 assert resp.status == 409 as "Email should be unique" 132 132 } 133 133 ··· 147 147 ]) 148 148 149 149 let with_auth = app_test.with_authorization(next: req) 150 - let resp = router.handle_request(with_auth, ctx) 150 + let resp = http_router.handle_request(with_auth, ctx) 151 151 assert resp.status == 409 as "Phone should be unique" 152 152 } 153 153 } ··· 156 156 let ctx = app_test.global_data() 157 157 158 158 let req = simulate.browser_request(http.Get, "/admin/users") 159 - let resp = router.handle_request(req, ctx) 159 + let resp = http_router.handle_request(req, ctx) 160 160 161 161 assert resp.status == 401 as "Access only provided to Admin users" 162 162 163 163 // ------------------------------------------------------- 164 164 let with_auth = app_test.with_authorization(next: req) 165 - let resp = router.handle_request(with_auth, ctx) 165 + let resp = http_router.handle_request(with_auth, ctx) 166 166 assert resp.status == 200 as "Endpoint access should be available for Admins" 167 167 168 168 let body = simulate.read_body(resp) ··· 208 208 ]) 209 209 210 210 let with_auth = app_test.with_authorization(new_user_req) 211 - let new_user_resp = router.handle_request(with_auth, ctx) 211 + let new_user_resp = http_router.handle_request(with_auth, ctx) 212 212 213 213 let assert Ok(_) = 214 214 json.parse(simulate.read_body(new_user_resp), { ··· 227 227 #("matricula", dummy_registration), 228 228 #("senha", dummy_password), 229 229 ]) 230 - let login_resp = router.handle_request(login_req, ctx) 230 + let login_resp = http_router.handle_request(login_req, ctx) 231 231 232 232 // UPDATING DUMMY ------------------------------------------------------------ 233 233 let new_name = wisp.random_string(8) ··· 245 245 ) 246 246 |> simulate.session(login_req, login_resp) 247 247 248 - let resp = router.handle_request(req, ctx) 248 + let resp = http_router.handle_request(req, ctx) 249 249 250 250 // ASSERTIONS ---------------------------------------------------------------- 251 251 ··· 281 281 ) 282 282 |> simulate.session(login_req, login_resp) 283 283 284 - let resp = router.handle_request(req, ctx) 284 + let resp = http_router.handle_request(req, ctx) 285 285 assert resp.status == 409 as "Status should be HTTP 409" 286 286 } 287 287 ··· 298 298 ]), 299 299 ) 300 300 |> simulate.session(login_req, login_resp) 301 - let resp = router.handle_request(req, ctx) 301 + let resp = http_router.handle_request(req, ctx) 302 302 assert resp.status == 409 as "Status should be HTTP 409" 303 303 } 304 304