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

:truck: move user queries to `shared` module

kacaii.dev 6b531b30 96159187

verified
+164 -73
+6 -1
client/manifest.toml
··· 3 3 4 4 packages = [ 5 5 { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, 6 + { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, 6 7 { name = "booklet", version = "1.1.0", build_tools = ["gleam"], requirements = [], otp_app = "booklet", source = "hex", outer_checksum = "08E0FDB78DC4D8A5D3C80295B021505C7D2A2E7B6C6D5EAB7286C36F4A53C851" }, 7 8 { name = "directories", version = "1.2.0", build_tools = ["gleam"], requirements = ["envoy", "gleam_stdlib", "platform", "simplifile"], otp_app = "directories", source = "hex", outer_checksum = "D13090CFCDF6759B87217E8DDD73A75903A700148A82C1D33799F333E249BF9E" }, 8 9 { name = "envoy", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "850DA9D29D2E5987735872A2B5C81035146D7FE19EFC486129E44440D03FD832" }, ··· 36 37 { name = "marceau", version = "1.3.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "2D1C27504BEF45005F5DFB18591F8610FB4BFA91744878210BDC464412EC44E9" }, 37 38 { name = "mist", version = "5.0.4", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "gleam_yielder", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "7CED4B2D81FD547ADB093D97B9928B9419A7F58B8562A30A6CC17A252B31AD05" }, 38 39 { name = "modem", version = "2.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib", "lustre"], otp_app = "modem", source = "hex", outer_checksum = "3F9682EBCBF4D26045F1038A7507E8C7967E49D43F9CA6BA68EF0C971B195A7F" }, 40 + { name = "opentelemetry_api", version = "1.5.0", build_tools = ["rebar3", "mix"], requirements = [], otp_app = "opentelemetry_api", source = "hex", outer_checksum = "F53EC8A1337AE4A487D43AC89DA4BD3A3C99DDF576655D071DEED8B56A2D5DDA" }, 41 + { name = "pg_types", version = "0.6.0", build_tools = ["rebar3"], requirements = [], otp_app = "pg_types", source = "hex", outer_checksum = "9949A4849DD13408FA249AB7B745E0D2DFDB9532AEE2B9722326E33CD082A778" }, 42 + { name = "pgo", version = "0.20.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "2F11E6649CEB38E569EF56B16BE1D04874AE5B11A02867080A2817CE423C683B" }, 39 43 { name = "platform", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "platform", source = "hex", outer_checksum = "8339420A95AD89AAC0F82F4C3DB8DD401041742D6C3F46132A8739F6AEB75391" }, 44 + { name = "pog", version = "4.1.0", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_otp", "gleam_stdlib", "gleam_time", "pgo"], otp_app = "pog", source = "hex", outer_checksum = "E4AFBA39A5FAA2E77291836C9683ADE882E65A06AB28CA7D61AE7A3AD61EBBD5" }, 40 45 { name = "polly", version = "3.0.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_erlang", "gleam_otp", "gleam_stdlib", "simplifile"], otp_app = "polly", source = "hex", outer_checksum = "35B11497B998618CEE216415A7853C3FED3F0F2148DC86BD8FC86B95D67F6DD8" }, 41 46 { name = "rsvp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_fetch", "gleam_http", "gleam_httpc", "gleam_javascript", "gleam_json", "gleam_stdlib", "lustre"], otp_app = "rsvp", source = "hex", outer_checksum = "40F9E0E662FF258E10C7041A9591261FE802D56625FB444B91510969644F7722" }, 42 - { name = "shared", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib", "youid"], source = "local", path = "../shared" }, 47 + { name = "shared", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib", "pog", "youid"], source = "local", path = "../shared" }, 43 48 { name = "simplifile", version = "2.3.2", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "E049B4DACD4D206D87843BCF4C775A50AE0F50A52031A2FFB40C9ED07D6EC70A" }, 44 49 { name = "snag", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "274F41D6C3ECF99F7686FDCE54183333E41D2C1CA5A3A673F9A8B2C7A4401077" }, 45 50 { name = "telemetry", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "7015FC8919DBE63764F4B4B87A95B7C0996BD539E0D499BE6EC9D7F3875B79E6" },
+4 -2
justfile
··· 28 28 just client::deps-update 29 29 just server::deps-update 30 30 31 + # Check for errors 31 32 [group("dev")] 32 33 lint: 33 - just shared::lint 34 - just client::lint 35 34 just server::lint 35 + just client::lint 36 + just shared::lint 36 37 38 + # Run unit tests 37 39 [group("dev")] 38 40 test: 39 41 just server::test
+1 -1
server/manifest.toml
··· 40 40 { name = "pgo", version = "0.20.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "2F11E6649CEB38E569EF56B16BE1D04874AE5B11A02867080A2817CE423C683B" }, 41 41 { name = "platform", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "platform", source = "hex", outer_checksum = "8339420A95AD89AAC0F82F4C3DB8DD401041742D6C3F46132A8739F6AEB75391" }, 42 42 { name = "pog", version = "4.1.0", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_otp", "gleam_stdlib", "gleam_time", "pgo"], otp_app = "pog", source = "hex", outer_checksum = "E4AFBA39A5FAA2E77291836C9683ADE882E65A06AB28CA7D61AE7A3AD61EBBD5" }, 43 - { name = "shared", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib", "youid"], source = "local", path = "../shared" }, 43 + { name = "shared", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib", "pog", "youid"], source = "local", path = "../shared" }, 44 44 { name = "simplifile", version = "2.3.2", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "E049B4DACD4D206D87843BCF4C775A50AE0F50A52031A2FFB40C9ED07D6EC70A" }, 45 45 { name = "splitter", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "3DFD6B6C49E61EDAF6F7B27A42054A17CFF6CA2135FF553D0CB61C234D281DD0" }, 46 46 { name = "squirrel", version = "4.6.0", build_tools = ["gleam"], requirements = ["argv", "envoy", "eval", "filepath", "glam", "gleam_community_ansi", "gleam_crypto", "gleam_json", "gleam_regexp", "gleam_stdlib", "gleam_time", "glexer", "justin", "mug", "non_empty_list", "pog", "simplifile", "term_size", "tom", "tote", "youid"], otp_app = "squirrel", source = "hex", outer_checksum = "0ED10A868BDD1A5D4B68D99CD1C72DC3F23C6E36E16D33454C5F0C31BAC9CB1E" },
-18
server/src/server/auth.gleam
··· 6 6 import pog 7 7 import server/auth/sql 8 8 import server/context 9 - import shared/user 10 9 import wisp 11 10 import youid/uuid 12 11 ··· 63 62 True -> Ok(row.id) 64 63 False -> Error(WrongPassword) 65 64 } 66 - } 67 - 68 - pub fn whoami( 69 - db: pog.Connection, 70 - uuid: uuid.Uuid, 71 - ) -> Result(user.User, AuthError) { 72 - use returned <- result.try( 73 - sql.whoami(db, uuid) 74 - |> result.map_error(Database), 75 - ) 76 - 77 - use row <- result.map( 78 - list.first(returned.rows) 79 - |> result.replace_error(NotFound), 80 - ) 81 - 82 - user.User(uuid:, full_name: row.full_name, email: row.email, phone: row.phone) 83 65 } 84 66 85 67 pub fn handle_error(err: AuthError) {
-38
server/src/server/auth/sql.gleam
··· 48 48 |> pog.execute(db) 49 49 } 50 50 51 - /// A row you get from running the `whoami` query 52 - /// defined in `./src/server/auth/sql/whoami.sql`. 53 - /// 54 - /// > ๐Ÿฟ๏ธ This type definition was generated automatically using v4.6.0 of the 55 - /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 56 - /// 57 - pub type WhoamiRow { 58 - WhoamiRow(id: Uuid, full_name: String, email: String, phone: String) 59 - } 60 - 61 - /// Runs the `whoami` query 62 - /// defined in `./src/server/auth/sql/whoami.sql`. 63 - /// 64 - /// > ๐Ÿฟ๏ธ This function was generated automatically using v4.6.0 of 65 - /// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 66 - /// 67 - pub fn whoami( 68 - db: pog.Connection, 69 - arg_1: Uuid, 70 - ) -> Result(pog.Returned(WhoamiRow), pog.QueryError) { 71 - let decoder = { 72 - use id <- decode.field(0, uuid_decoder()) 73 - use full_name <- decode.field(1, decode.string) 74 - use email <- decode.field(2, decode.string) 75 - use phone <- decode.field(3, decode.string) 76 - decode.success(WhoamiRow(id:, full_name:, email:, phone:)) 77 - } 78 - 79 - "select u.id, u.full_name, u.email, u.phone 80 - from public.user_account as u 81 - where u.id = $1; 82 - " 83 - |> pog.query 84 - |> pog.parameter(pog.text(uuid.to_string(arg_1))) 85 - |> pog.returning(decoder) 86 - |> pog.execute(db) 87 - } 88 - 89 51 // --- Encoding/decoding utils ------------------------------------------------- 90 52 91 53 /// A decoder to decode `Uuid`s coming from a Postgres query.
-3
server/src/server/auth/sql/whoami.sql
··· 1 - select u.id, u.full_name, u.email, u.phone 2 - from public.user_account as u 3 - where u.id = $1;
+10 -2
server/src/server/router/login.gleam
··· 8 8 import server/auth 9 9 import server/context.{type Context} 10 10 import shared/session 11 + import shared/user 11 12 import wisp.{type Request, type Response} 12 13 import youid/uuid 13 14 14 15 type LoginError { 15 16 Auth(auth.AuthError) 17 + QueryError(user.UserError) 16 18 } 17 19 18 20 /// ๎ธฌ Queries the database and store a session token containing the user uuid ··· 44 46 ) -> Response { 45 47 let query_result = { 46 48 use user <- result.map( 47 - auth.whoami(db, user_uuid) 48 - |> result.map_error(Auth), 49 + user.find(db, user_uuid) 50 + |> result.map_error(QueryError), 49 51 ) 50 52 51 53 session.Authenticated(user) ··· 72 74 fn handle_error(err: LoginError) -> Response { 73 75 case err { 74 76 Auth(err) -> auth.handle_error(err) 77 + 78 + QueryError(err) -> 79 + case err { 80 + user.NotFound -> wisp.not_found() 81 + user.Database(_) -> wisp.internal_server_error() 82 + } 75 83 } 76 84 } 77 85
+10 -5
server/src/server/router/whoami.gleam
··· 4 4 import server/auth 5 5 import server/context.{type Context} 6 6 import shared/session 7 + import shared/user 7 8 import wisp.{type Request, type Response} 8 9 import youid/uuid 9 10 10 11 type WhoamiError { 11 - Auth(auth.AuthError) 12 + UserError(user.UserError) 12 13 MissingToken 13 14 InvalidUuidFormat 14 15 NotFound ··· 30 31 |> result.replace_error(InvalidUuidFormat), 31 32 ) 32 33 33 - auth.whoami(ctx.db, uuid) 34 - |> result.map_error(Auth) 34 + user.find(ctx.db, uuid) 35 + |> result.map_error(UserError) 35 36 } 36 37 37 38 case query_result { ··· 47 48 fn handle_error(err: WhoamiError) -> Response { 48 49 case err { 49 50 InvalidUuidFormat -> wisp.bad_request("Invalid Uuid format") 51 + NotFound -> wisp.not_found() 50 52 51 53 MissingToken -> 52 54 "Missing token" 53 55 |> wisp.Text 54 56 |> wisp.set_body(wisp.response(401), _) 55 57 56 - Auth(err) -> auth.handle_error(err) 57 - NotFound -> wisp.not_found() 58 + UserError(err) -> 59 + case err { 60 + user.NotFound -> wisp.not_found() 61 + user.Database(_) -> wisp.internal_server_error() 62 + } 58 63 } 59 64 }
+2
shared/gleam.toml
··· 16 16 gleam_stdlib = ">= 0.44.0 and < 2.0.0" 17 17 gleam_json = ">= 3.1.0 and < 4.0.0" 18 18 youid = ">= 1.5.1 and < 2.0.0" 19 + pog = ">= 4.1.0 and < 5.0.0" 19 20 20 21 [dev-dependencies] 21 22 gleeunit = ">= 1.0.0 and < 2.0.0" 23 + squirrel = ">= 4.6.0 and < 5.0.0"
+4 -1
shared/justfile
··· 5 5 deps-update: 6 6 gleam deps update 7 7 8 - 9 8 # Lint project 10 9 lint: 11 10 gleam check 11 + 12 + # Generate queries 13 + sql: 14 + gleam run -m squirrel
+28
shared/manifest.toml
··· 2 2 # You typically do not need to edit this file 3 3 4 4 packages = [ 5 + { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, 6 + { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, 7 + { name = "envoy", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "850DA9D29D2E5987735872A2B5C81035146D7FE19EFC486129E44440D03FD832" }, 8 + { name = "eval", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "eval", source = "hex", outer_checksum = "264DAF4B49DF807F303CA4A4E4EBC012070429E40BE384C58FE094C4958F9BDA" }, 9 + { name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" }, 10 + { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" }, 11 + { name = "glam", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "237C2CE218A2A0A5D46D625F8EF5B78F964BC91018B78D692B17E1AB84295229" }, 12 + { name = "gleam_community_ansi", version = "1.4.4", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_regexp", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "1B3AEA6074AB34D5F0674744F36DDC7290303A03295507E2DEC61EDD6F5777FE" }, 13 + { name = "gleam_community_colour", version = "2.0.4", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "6DB4665555D7D2B27F0EA32EF47E8BEBC4303821765F9C73D483F38EE24894F0" }, 5 14 { name = "gleam_crypto", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "50774BAFFF1144E7872814C566C5D653D83A3EBF23ACC3156B757A1B6819086E" }, 15 + { name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" }, 6 16 { name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" }, 17 + { name = "gleam_otp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BA6A294E295E428EC1562DC1C11EA7530DCB981E8359134BEABC8493B7B2258E" }, 18 + { name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" }, 7 19 { name = "gleam_stdlib", version = "0.69.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "AAB0962BEBFAA67A2FBEE9EEE218B057756808DC9AF77430F5182C6115B3A315" }, 8 20 { name = "gleam_time", version = "1.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "56DB0EF9433826D3B99DB0B4AF7A2BFED13D09755EC64B1DAAB46F804A9AD47D" }, 9 21 { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, 22 + { name = "glexer", version = "2.3.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "splitter"], otp_app = "glexer", source = "hex", outer_checksum = "41D8D2E855AEA87ADC94B7AF26A5FEA3C90268D4CF2CCBBD64FD6863714EE085" }, 23 + { name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" }, 24 + { name = "mug", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "mug", source = "hex", outer_checksum = "C01279D98E40371DA23461774B63F0E3581B8F1396049D881B0C7EB32799D93F" }, 25 + { name = "non_empty_list", version = "2.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "non_empty_list", source = "hex", outer_checksum = "1CA43D18C07E98E9ED5A60D9CB2FFE0FF40DEFFA45D58A3FF589589F05658F7B" }, 26 + { name = "opentelemetry_api", version = "1.5.0", build_tools = ["rebar3", "mix"], requirements = [], otp_app = "opentelemetry_api", source = "hex", outer_checksum = "F53EC8A1337AE4A487D43AC89DA4BD3A3C99DDF576655D071DEED8B56A2D5DDA" }, 27 + { name = "pg_types", version = "0.6.0", build_tools = ["rebar3"], requirements = [], otp_app = "pg_types", source = "hex", outer_checksum = "9949A4849DD13408FA249AB7B745E0D2DFDB9532AEE2B9722326E33CD082A778" }, 28 + { name = "pgo", version = "0.20.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "2F11E6649CEB38E569EF56B16BE1D04874AE5B11A02867080A2817CE423C683B" }, 29 + { name = "pog", version = "4.1.0", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_otp", "gleam_stdlib", "gleam_time", "pgo"], otp_app = "pog", source = "hex", outer_checksum = "E4AFBA39A5FAA2E77291836C9683ADE882E65A06AB28CA7D61AE7A3AD61EBBD5" }, 30 + { name = "simplifile", version = "2.3.2", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "E049B4DACD4D206D87843BCF4C775A50AE0F50A52031A2FFB40C9ED07D6EC70A" }, 31 + { name = "splitter", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "3DFD6B6C49E61EDAF6F7B27A42054A17CFF6CA2135FF553D0CB61C234D281DD0" }, 32 + { name = "squirrel", version = "4.6.0", build_tools = ["gleam"], requirements = ["argv", "envoy", "eval", "filepath", "glam", "gleam_community_ansi", "gleam_crypto", "gleam_json", "gleam_regexp", "gleam_stdlib", "gleam_time", "glexer", "justin", "mug", "non_empty_list", "pog", "simplifile", "term_size", "tom", "tote", "youid"], otp_app = "squirrel", source = "hex", outer_checksum = "0ED10A868BDD1A5D4B68D99CD1C72DC3F23C6E36E16D33454C5F0C31BAC9CB1E" }, 33 + { name = "term_size", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "term_size", source = "hex", outer_checksum = "D00BD2BC8FB3EBB7E6AE076F3F1FF2AC9D5ED1805F004D0896C784D06C6645F1" }, 34 + { name = "tom", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_time"], otp_app = "tom", source = "hex", outer_checksum = "90791DA4AACE637E30081FE77049B8DB850FBC8CACC31515376BCC4E59BE1DD2" }, 35 + { name = "tote", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tote", source = "hex", outer_checksum = "A249892E26A53C668897F8D47845B0007EEE07707A1A03437487F0CD5A452CA5" }, 10 36 { name = "youid", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_stdlib", "gleam_time"], otp_app = "youid", source = "hex", outer_checksum = "580E909FD704DB16416D5CB080618EDC2DA0F1BE4D21B490C0683335E3FFC5AF" }, 11 37 ] 12 38 ··· 14 40 gleam_json = { version = ">= 3.1.0 and < 4.0.0" } 15 41 gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } 16 42 gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 43 + pog = { version = ">= 4.1.0 and < 5.0.0" } 44 + squirrel = { version = ">= 4.6.0 and < 5.0.0" } 17 45 youid = { version = ">= 1.5.1 and < 2.0.0" }
+27 -2
shared/src/shared/user.gleam
··· 1 1 import gleam/dynamic/decode 2 2 import gleam/json 3 + import gleam/list 4 + import gleam/result 5 + import pog 6 + import shared/user/sql 3 7 import youid/uuid 8 + 9 + pub type UserError { 10 + Database(pog.QueryError) 11 + NotFound 12 + } 4 13 5 14 pub type User { 6 15 User(uuid: uuid.Uuid, full_name: String, email: String, phone: String) ··· 23 32 decode.success(User(uuid:, full_name:, email:, phone:)) 24 33 } 25 34 35 + /// Query an user by Uuid 36 + pub fn find(db: pog.Connection, uuid: uuid.Uuid) -> Result(User, UserError) { 37 + use returned <- result.try( 38 + sql.find_by_id(db, uuid) 39 + |> result.map_error(Database), 40 + ) 41 + 42 + use row <- result.map( 43 + list.first(returned.rows) 44 + |> result.replace_error(NotFound), 45 + ) 46 + 47 + User(uuid:, full_name: row.full_name, email: row.email, phone: row.phone) 48 + } 49 + 50 + // helpers 51 + 26 52 fn uuid_decoder() -> decode.Decoder(uuid.Uuid) { 27 53 use value <- decode.then(decode.string) 28 54 case uuid.from_string(value) { ··· 32 58 } 33 59 34 60 fn uuid_to_json(uuid: uuid.Uuid) { 35 - uuid.to_string(uuid) 36 - |> json.string() 61 + uuid.to_string(uuid) |> json.string() 37 62 }
+64
shared/src/shared/user/sql.gleam
··· 1 + //// This module contains the code to run the sql queries defined in 2 + //// `./src/shared/user/sql`. 3 + //// > ๐Ÿฟ๏ธ This module was generated automatically using v4.6.0 of 4 + //// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 5 + //// 6 + 7 + import gleam/dynamic/decode 8 + import pog 9 + import youid/uuid.{type Uuid} 10 + 11 + /// A row you get from running the `find_by_id` query 12 + /// defined in `./src/shared/user/sql/find_by_id.sql`. 13 + /// 14 + /// > ๐Ÿฟ๏ธ This type definition was generated automatically using v4.6.0 of the 15 + /// > [squirrel package](https://github.com/giacomocavalieri/squirrel). 16 + /// 17 + pub type FindByIdRow { 18 + FindByIdRow(id: Uuid, full_name: String, email: String, phone: String) 19 + } 20 + 21 + /// Runs the `find_by_id` query 22 + /// defined in `./src/shared/user/sql/find_by_id.sql`. 23 + /// 24 + /// > ๐Ÿฟ๏ธ This function was generated automatically using v4.6.0 of 25 + /// > the [squirrel package](https://github.com/giacomocavalieri/squirrel). 26 + /// 27 + pub fn find_by_id( 28 + db: pog.Connection, 29 + arg_1: Uuid, 30 + ) -> Result(pog.Returned(FindByIdRow), pog.QueryError) { 31 + let decoder = { 32 + use id <- decode.field(0, uuid_decoder()) 33 + use full_name <- decode.field(1, decode.string) 34 + use email <- decode.field(2, decode.string) 35 + use phone <- decode.field(3, decode.string) 36 + decode.success(FindByIdRow(id:, full_name:, email:, phone:)) 37 + } 38 + 39 + "select 40 + u.id, 41 + u.full_name, 42 + u.email, 43 + u.phone 44 + from public.user_account as u 45 + where u.id = 46 + $1; 47 + " 48 + |> pog.query 49 + |> pog.parameter(pog.text(uuid.to_string(arg_1))) 50 + |> pog.returning(decoder) 51 + |> pog.execute(db) 52 + } 53 + 54 + // --- Encoding/decoding utils ------------------------------------------------- 55 + 56 + /// A decoder to decode `Uuid`s coming from a Postgres query. 57 + /// 58 + fn uuid_decoder() { 59 + use bit_array <- decode.then(decode.bit_array) 60 + case uuid.from_bit_array(bit_array) { 61 + Ok(uuid) -> decode.success(uuid) 62 + Error(_) -> decode.failure(uuid.v7(), "Uuid") 63 + } 64 + }
+8
shared/src/shared/user/sql/find_by_id.sql
··· 1 + select 2 + u.id, 3 + u.full_name, 4 + u.email, 5 + u.phone 6 + from public.user_account as u 7 + where u.id = 8 + $1;