From e0b287c96c5c9a9ff47fc65c749037fb21d20985 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Sat, 19 Oct 2024 19:50:47 +0530 Subject: [PATCH 01/24] chore: setting up some stuff --- gleam.toml | 2 ++ manifest.toml | 8 ++++++++ src/config.gleam | 9 +++++++++ src/my_web_app.gleam | 22 ++++++++++++++++++++-- 4 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 src/config.gleam diff --git a/gleam.toml b/gleam.toml index ca93e28..8447d15 100644 --- a/gleam.toml +++ b/gleam.toml @@ -17,6 +17,8 @@ gleam_stdlib = ">= 0.34.0 and < 2.0.0" mist = ">= 1.2.0 and < 2.0.0" gleam_http = ">= 3.6.0 and < 4.0.0" gleam_erlang = ">= 0.25.0 and < 1.0.0" +gleam_pgo = ">= 0.14.0 and < 1.0.0" +envoy = ">= 1.0.1 and < 2.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index 654b585..24beecc 100644 --- a/manifest.toml +++ b/manifest.toml @@ -2,11 +2,14 @@ # You typically do not need to edit this file packages = [ + { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, { name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" }, + { name = "envoy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "CFAACCCFC47654F7E8B75E614746ED924C65BD08B1DE21101548AC314A8B6A41" }, { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, + { name = "gleam_pgo", version = "0.14.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "pgo"], otp_app = "gleam_pgo", source = "hex", outer_checksum = "12256A7F351E994A6E43AB67276DD8AACE752991C013F48A288C4A848F3A9758" }, { name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" }, { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" }, @@ -14,12 +17,17 @@ packages = [ { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, { name = "mist", version = "1.2.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "109B4D64E68C104CC23BB3CC5441ECD479DD7444889DA01113B75C6AF0F0E17B" }, + { name = "opentelemetry_api", version = "1.4.0", build_tools = ["rebar3", "mix"], requirements = [], otp_app = "opentelemetry_api", source = "hex", outer_checksum = "3DFBBFAA2C2ED3121C5C483162836C4F9027DEF469C41578AF5EF32589FCFC58" }, + { name = "pg_types", version = "0.4.0", build_tools = ["rebar3"], requirements = [], otp_app = "pg_types", source = "hex", outer_checksum = "B02EFA785CAECECF9702C681C80A9CA12A39F9161A846CE17B01FB20AEEED7EB" }, + { name = "pgo", version = "0.14.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "71016C22599936E042DC0012EE4589D24C71427D266292F775EBF201D97DF9C9" }, { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, ] [requirements] +envoy = { version = ">= 1.0.1 and < 2.0.0" } gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } gleam_http = { version = ">= 3.6.0 and < 4.0.0" } +gleam_pgo = { version = ">= 0.14.0 and < 1.0.0" } gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } gleeunit = { version = ">= 1.0.0 and < 2.0.0" } mist = { version = ">= 1.2.0 and < 2.0.0" } diff --git a/src/config.gleam b/src/config.gleam new file mode 100644 index 0000000..60c2ea8 --- /dev/null +++ b/src/config.gleam @@ -0,0 +1,9 @@ +import envoy +import gleam/pgo +import gleam/result + +pub fn read_connection_uri() -> Result(pgo.Connection, Nil) { + use database_url <- result.try(envoy.get("DATABASE_URL")) + use config <- result.try(pgo.url_config(database_url)) + Ok(pgo.connect(config)) +} diff --git a/src/my_web_app.gleam b/src/my_web_app.gleam index e1bf5bf..35489e1 100644 --- a/src/my_web_app.gleam +++ b/src/my_web_app.gleam @@ -2,17 +2,35 @@ import mist import gleam/erlang/process import gleam/bytes_builder import gleam/http/response.{Response} +import gleam/string +import gleam/io +import gleam/int +import gleam/erlang/os +import gleam/result pub fn main() { + let port = + os.get_env("PORT") + |> result.then(int.parse) + |> result.unwrap(3000) + let assert Ok(_) = web_service |> mist.new - |> mist.port(8080) + |> mist.port(port) |> mist.start_http + + ["Started listening on localhost:", int.to_string(port), " ✨"] + |> string.concat + |> io.println + process.sleep_forever() } fn web_service(_request) { - let body = bytes_builder.from_string("Hello, Zerops!") + let body = bytes_builder.from_string( + "This is a simple, basic Gleam mist application running on Zerops.io, each request adds an entry to the PostgreSQL database and returns a count. + See the source repository (https://github.com/zeropsio/recipe-gleam) for more information." + ) Response(200, [], mist.Bytes(body)) } From 0fd568cbff7fe4c9f36ce1d56e7c4a5e50b09bdd Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Sun, 20 Oct 2024 21:58:21 +0530 Subject: [PATCH 02/24] chore: fix config with better err handling --- .gitignore | 1 + src/config.gleam | 40 ++++++++++++++++++++++++++++++---- src/my_web_app.gleam | 51 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 599be4e..1f75072 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.ez /build erl_crash.dump +.env diff --git a/src/config.gleam b/src/config.gleam index 60c2ea8..6814b9d 100644 --- a/src/config.gleam +++ b/src/config.gleam @@ -1,9 +1,41 @@ import envoy import gleam/pgo import gleam/result +import gleam/io pub fn read_connection_uri() -> Result(pgo.Connection, Nil) { - use database_url <- result.try(envoy.get("DATABASE_URL")) - use config <- result.try(pgo.url_config(database_url)) - Ok(pgo.connect(config)) -} + io.println("Attempting to connect to database...") + + use database_url <- result.try({ + case envoy.get("DATABASE_URL") { + Ok(url) -> { + io.println("Found DATABASE_URL configuration") + Ok(url) + } + Error(_) -> { + io.println("Failed to get DATABASE_URL") + Error(Nil) + } + } + }) + + use config <- result.try({ + case pgo.url_config(database_url) { + Ok(cfg) -> { + io.println("Database configuration parsed successfully") + Ok(cfg) + } + Error(_) -> { + io.println("Failed to parse database configuration") + Error(Nil) + } + } + }) + + case pgo.connect(config) { + connection -> { + io.println("Successfully connected to database! ✨") + Ok(connection) + } + } +} diff --git a/src/my_web_app.gleam b/src/my_web_app.gleam index 35489e1..c180cee 100644 --- a/src/my_web_app.gleam +++ b/src/my_web_app.gleam @@ -7,6 +7,7 @@ import gleam/io import gleam/int import gleam/erlang/os import gleam/result +import config pub fn main() { let port = @@ -28,9 +29,53 @@ pub fn main() { } fn web_service(_request) { + // Check database connection using config + let db_status = case config.read_connection_uri() { + Ok(_) -> "Database connection successful! ✅" + Error(_) -> "Database connection failed! ❌" + } + let body = bytes_builder.from_string( - "This is a simple, basic Gleam mist application running on Zerops.io, each request adds an entry to the PostgreSQL database and returns a count. - See the source repository (https://github.com/zeropsio/recipe-gleam) for more information." + " + + + + Gleam Mist Application on Zerops + + + +

Gleam Mist Application

+
case config.read_connection_uri() { + Ok(_) -> "success" + Error(_) -> "error" + } <> "\"> + " <> db_status <> " +
+

This is a simple, basic Gleam mist application running on Zerops.io, each request adds an entry to the PostgreSQL database and returns a count.

+

See the source repository for more information.

+ + " ) - Response(200, [], mist.Bytes(body)) + + Response(200, [#("Content-Type", "text/html")], mist.Bytes(body)) } From 0489772a275639715e7ea6ce11d816d47629ef97 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Sun, 20 Oct 2024 23:55:23 +0530 Subject: [PATCH 03/24] fix env issues --- gleam.toml | 4 ++-- manifest.toml | 7 +++++-- src/{my_web_app.gleam => app.gleam} | 0 src/config.gleam | 9 ++++++--- 4 files changed, 13 insertions(+), 7 deletions(-) rename src/{my_web_app.gleam => app.gleam} (100%) diff --git a/gleam.toml b/gleam.toml index 8447d15..4c25294 100644 --- a/gleam.toml +++ b/gleam.toml @@ -1,4 +1,4 @@ -name = "my_web_app" +name = "app" version = "1.0.0" # Fill out these fields if you intend to generate HTML documentation or publish @@ -18,7 +18,7 @@ mist = ">= 1.2.0 and < 2.0.0" gleam_http = ">= 3.6.0 and < 4.0.0" gleam_erlang = ">= 0.25.0 and < 1.0.0" gleam_pgo = ">= 0.14.0 and < 1.0.0" -envoy = ">= 1.0.1 and < 2.0.0" +glenvy = ">= 1.0.1 and < 2.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index 24beecc..2e91f89 100644 --- a/manifest.toml +++ b/manifest.toml @@ -4,7 +4,7 @@ packages = [ { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, { name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" }, - { name = "envoy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "CFAACCCFC47654F7E8B75E614746ED924C65BD08B1DE21101548AC314A8B6A41" }, + { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, @@ -12,22 +12,25 @@ packages = [ { name = "gleam_pgo", version = "0.14.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "pgo"], otp_app = "gleam_pgo", source = "hex", outer_checksum = "12256A7F351E994A6E43AB67276DD8AACE752991C013F48A288C4A848F3A9758" }, { name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" }, { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, + { name = "glenvy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib", "nibble", "simplifile"], otp_app = "glenvy", source = "hex", outer_checksum = "0F95149BF3052724BC2C76DF0235ED12C681683936294C84E2FB7853254FFB00" }, { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" }, { name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" }, { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, { name = "mist", version = "1.2.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "109B4D64E68C104CC23BB3CC5441ECD479DD7444889DA01113B75C6AF0F0E17B" }, + { name = "nibble", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "nibble", source = "hex", outer_checksum = "67C6BEBC1AB6D771AB893B4A7B3E66C92668C6E7774C335FEFCD545B06435FE5" }, { name = "opentelemetry_api", version = "1.4.0", build_tools = ["rebar3", "mix"], requirements = [], otp_app = "opentelemetry_api", source = "hex", outer_checksum = "3DFBBFAA2C2ED3121C5C483162836C4F9027DEF469C41578AF5EF32589FCFC58" }, { name = "pg_types", version = "0.4.0", build_tools = ["rebar3"], requirements = [], otp_app = "pg_types", source = "hex", outer_checksum = "B02EFA785CAECECF9702C681C80A9CA12A39F9161A846CE17B01FB20AEEED7EB" }, { name = "pgo", version = "0.14.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "71016C22599936E042DC0012EE4589D24C71427D266292F775EBF201D97DF9C9" }, { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, + { name = "simplifile", version = "2.2.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0DFABEF7DC7A9E2FF4BB27B108034E60C81BEBFCB7AB816B9E7E18ED4503ACD8" }, ] [requirements] -envoy = { version = ">= 1.0.1 and < 2.0.0" } gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } gleam_http = { version = ">= 3.6.0 and < 4.0.0" } gleam_pgo = { version = ">= 0.14.0 and < 1.0.0" } gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } gleeunit = { version = ">= 1.0.0 and < 2.0.0" } +glenvy = { version = ">= 1.0.1 and < 2.0.0" } mist = { version = ">= 1.2.0 and < 2.0.0" } diff --git a/src/my_web_app.gleam b/src/app.gleam similarity index 100% rename from src/my_web_app.gleam rename to src/app.gleam diff --git a/src/config.gleam b/src/config.gleam index 6814b9d..b31a61f 100644 --- a/src/config.gleam +++ b/src/config.gleam @@ -1,13 +1,16 @@ -import envoy +import glenvy/dotenv +import glenvy/env import gleam/pgo import gleam/result import gleam/io pub fn read_connection_uri() -> Result(pgo.Connection, Nil) { io.println("Attempting to connect to database...") - + + let _ = dotenv.load() + // Setup glenvy below use database_url <- result.try({ - case envoy.get("DATABASE_URL") { + case env.get_string("DATABASE_URL") { Ok(url) -> { io.println("Found DATABASE_URL configuration") Ok(url) From a168e01f91c95824e04b691f7a6a6cc72a239197 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Mon, 21 Oct 2024 11:16:27 +0530 Subject: [PATCH 04/24] chore: setting up some stuff --- gleam.toml | 1 + manifest.toml | 2 ++ src/app.gleam | 71 ++++++++++++++++++++++++++++++++++------- src/config.gleam | 61 ++++++++++++++--------------------- src/database.gleam | 79 ++++++++++++++++++++++++++++++++++++++++++++++ zerops.yml | 10 +++--- 6 files changed, 172 insertions(+), 52 deletions(-) create mode 100644 src/database.gleam diff --git a/gleam.toml b/gleam.toml index 4c25294..b4fdd1d 100644 --- a/gleam.toml +++ b/gleam.toml @@ -19,6 +19,7 @@ gleam_http = ">= 3.6.0 and < 4.0.0" gleam_erlang = ">= 0.25.0 and < 1.0.0" gleam_pgo = ">= 0.14.0 and < 1.0.0" glenvy = ">= 1.0.1 and < 2.0.0" +envoy = ">= 1.0.1 and < 2.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index 2e91f89..c6ed1e0 100644 --- a/manifest.toml +++ b/manifest.toml @@ -4,6 +4,7 @@ packages = [ { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, { name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" }, + { name = "envoy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "CFAACCCFC47654F7E8B75E614746ED924C65BD08B1DE21101548AC314A8B6A41" }, { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, @@ -27,6 +28,7 @@ packages = [ ] [requirements] +envoy = { version = ">= 1.0.1 and < 2.0.0" } gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } gleam_http = { version = ">= 3.6.0 and < 4.0.0" } gleam_pgo = { version = ">= 0.14.0 and < 1.0.0" } diff --git a/src/app.gleam b/src/app.gleam index c180cee..150c1ee 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -7,34 +7,74 @@ import gleam/io import gleam/int import gleam/erlang/os import gleam/result +import glenvy/dotenv +import glenvy/env +import database import config pub fn main() { + // Load and verify environment variables at startup + case dotenv.load() { +Ok(_) -> { + io.println("Environment variables loaded") + case env.get_string("DATABASE_URL") { + Ok(_) -> io.println("DATABASE_URL is set") + Error(_) -> io.println("DATABASE_URL is not set") + } +} + Error(_) -> { + io.println("Failed to load environment variables: ") + } + } + let port = os.get_env("PORT") |> result.then(int.parse) |> result.unwrap(3000) - + let assert Ok(_) = web_service |> mist.new |> mist.port(port) |> mist.start_http - + ["Started listening on localhost:", int.to_string(port), " ✨"] |> string.concat |> io.println - + process.sleep_forever() } fn web_service(_request) { + io.println("Received a new request") + // Check database connection using config - let db_status = case config.read_connection_uri() { - Ok(_) -> "Database connection successful! ✅" - Error(_) -> "Database connection failed! ❌" + let #(db_status, entry_count) = case config.get_db_connection() { + Ok(connection) -> { + io.println("Database connection successful") + // Use the database module to add entry and get count + case database.add_entry_and_get_count(connection) { + Ok(count) -> { + io.println("Successfully added entry and got count: " <> int.to_string(count)) + #("Database connection successful! ✅", count) + } + Error(err) -> { + io.println("Database operation failed: " <> err) + #("Database operation failed: " <> err, 0) + } + } + } + Error(err) -> { + let error_message = case err { + Nil -> "Unknown database connection error" + } + io.println(error_message) + #(error_message <> " ❌", 0) + } } - + + io.println("Preparing response") + let body = bytes_builder.from_string( " @@ -61,21 +101,30 @@ fn web_service(_request) { background-color: #ffe6e6; border: 1px solid #cc0000; } + .count { + font-size: 24px; + font-weight: bold; + margin: 20px 0; + }

Gleam Mist Application

-
case config.read_connection_uri() { - Ok(_) -> "success" - Error(_) -> "error" +
case string.contains(db_status, "successful") { + True -> "success" + False -> "error" } <> "\"> " <> db_status <> "
+
+ Total entries: " <> int.to_string(entry_count) <> " +

This is a simple, basic Gleam mist application running on Zerops.io, each request adds an entry to the PostgreSQL database and returns a count.

See the source repository for more information.

" ) - + + io.println("Sending response") Response(200, [#("Content-Type", "text/html")], mist.Bytes(body)) } diff --git a/src/config.gleam b/src/config.gleam index b31a61f..d74d3ef 100644 --- a/src/config.gleam +++ b/src/config.gleam @@ -1,44 +1,31 @@ -import glenvy/dotenv -import glenvy/env import gleam/pgo -import gleam/result import gleam/io +import glenvy/dotenv +import glenvy/env -pub fn read_connection_uri() -> Result(pgo.Connection, Nil) { - io.println("Attempting to connect to database...") - - let _ = dotenv.load() - // Setup glenvy below - use database_url <- result.try({ - case env.get_string("DATABASE_URL") { - Ok(url) -> { - io.println("Found DATABASE_URL configuration") - Ok(url) - } - Error(_) -> { - io.println("Failed to get DATABASE_URL") - Error(Nil) - } - } - }) - - use config <- result.try({ - case pgo.url_config(database_url) { - Ok(cfg) -> { - io.println("Database configuration parsed successfully") - Ok(cfg) - } - Error(_) -> { - io.println("Failed to parse database configuration") - Error(Nil) +pub fn get_db_connection() -> Result(pgo.Connection, Nil) { + case dotenv.load() { + Ok(_) -> io.println("Environment variables loaded successfully") + Error(_) -> io.println("Warning: Could not load .env file") + } + + case env.get_string("DATABASE_URL") { + Ok(database_url) -> { + case pgo.url_config(database_url) { + Ok(config) -> { + let connection = pgo.connect(config) + io.println("Successfully connected to database! ✨") + Ok(connection) + } + Error(_) -> { + io.println("Failed to parse database configuration") + Error(Nil) + } } } - }) - - case pgo.connect(config) { - connection -> { - io.println("Successfully connected to database! ✨") - Ok(connection) + Error(_) -> { + io.println("Failed to get DATABASE_URL from environment") + Error(Nil) } } -} +} \ No newline at end of file diff --git a/src/database.gleam b/src/database.gleam new file mode 100644 index 0000000..06ac07e --- /dev/null +++ b/src/database.gleam @@ -0,0 +1,79 @@ +import gleam/string +import gleam/pgo +import gleam/io +import gleam/dynamic +import gleam/int + +pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(Int, String) { + io.println("Adding entry and getting count") + // Insert a new entry + let insert_query = "INSERT INTO entries (created_at) VALUES (NOW())" + io.println("Executing insert query: " <> insert_query) + case pgo.execute( + query: insert_query, + on: conn, + with: [], + expecting: dynamic.dynamic + ) { + Ok(_) -> { + io.println("Entry added successfully") + get_count(conn) + } + Error(err) -> { + let error_message = query_error_to_string(err) + io.println("Failed to add entry: " <> error_message) + Error("Failed to add entry: " <> error_message) + } + } +} + +fn get_count(conn: pgo.Connection) -> Result(Int, String) { + let count_query = "SELECT COUNT(*) FROM entries" + io.println("Executing count query: " <> count_query) + case pgo.execute( + query: count_query, + on: conn, + with: [], + expecting: dynamic.int + ) { + Ok(result) -> { + case result.rows { + [count] -> { + io.println("Count retrieved successfully: " <> int.to_string(count)) + Ok(count) + } + _ -> { + io.println("Unexpected result format") + Error("Unexpected result format") + } + } + } + Error(err) -> { + let error_message = query_error_to_string(err) + io.println("Failed to get count: " <> error_message) + Error("Failed to get count: " <> error_message) + } + } +} + +fn query_error_to_string(error: pgo.QueryError) -> String { + case error { + pgo.ConstraintViolated(message: message, constraint: constraint, detail: detail) -> + "Constraint violated: " <> message <> " (Constraint: " <> constraint <> ", Detail: " <> detail <> ")" + + pgo.PostgresqlError(code: code, name: name, message: message) -> + "PostgreSQL error: " <> message <> " (Code: " <> code <> ", Name: " <> name <> ")" + + pgo.UnexpectedArgumentCount(expected: expected, got: got) -> + "Unexpected argument count. Expected: " <> int.to_string(expected) <> ", Got: " <> int.to_string(got) + + pgo.UnexpectedArgumentType(expected: expected, got: got) -> + "Unexpected argument type. Expected: " <> expected <> ", Got: " <> got + + pgo.UnexpectedResultType(errors) -> + "Unexpected result type: " <> string.inspect(errors) + + pgo.ConnectionUnavailable -> + "Connection unavailable" + } +} \ No newline at end of file diff --git a/zerops.yml b/zerops.yml index c6e5387..f617ec4 100644 --- a/zerops.yml +++ b/zerops.yml @@ -3,11 +3,13 @@ zerops: build: base: gleam@1.5 buildCommands: - - gleam export erlang-shipment - deployFiles: build/~erlang-shipment + - gleam build + deployFiles: / run: base: gleam@1.5 + envVariables: + DATABASE_URL: postgresql://${db_user}:${db_password}@${db_hostname}:${db_port} ports: - - port: 8080 + - port: 3000 httpSupport: true - start: ./erlang-shipment/entrypoint.sh run + start: gleam run From df8907049e1d48c463f42c42f0c0104f071ddf21 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Mon, 21 Oct 2024 11:16:51 +0530 Subject: [PATCH 05/24] chore: remove unused pkg --- gleam.toml | 1 - manifest.toml | 2 -- 2 files changed, 3 deletions(-) diff --git a/gleam.toml b/gleam.toml index b4fdd1d..4c25294 100644 --- a/gleam.toml +++ b/gleam.toml @@ -19,7 +19,6 @@ gleam_http = ">= 3.6.0 and < 4.0.0" gleam_erlang = ">= 0.25.0 and < 1.0.0" gleam_pgo = ">= 0.14.0 and < 1.0.0" glenvy = ">= 1.0.1 and < 2.0.0" -envoy = ">= 1.0.1 and < 2.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index c6ed1e0..2e91f89 100644 --- a/manifest.toml +++ b/manifest.toml @@ -4,7 +4,6 @@ packages = [ { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, { name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" }, - { name = "envoy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "CFAACCCFC47654F7E8B75E614746ED924C65BD08B1DE21101548AC314A8B6A41" }, { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, @@ -28,7 +27,6 @@ packages = [ ] [requirements] -envoy = { version = ">= 1.0.1 and < 2.0.0" } gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } gleam_http = { version = ">= 3.6.0 and < 4.0.0" } gleam_pgo = { version = ">= 0.14.0 and < 1.0.0" } From 3a62795dcff5af5bd440736156cf3c5facb3b700 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Mon, 21 Oct 2024 11:25:51 +0530 Subject: [PATCH 06/24] chore: setup zerops yamls --- zerops-project-import.yml | 3 +++ zerops.yml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/zerops-project-import.yml b/zerops-project-import.yml index 6074bb9..6c8fc5b 100644 --- a/zerops-project-import.yml +++ b/zerops-project-import.yml @@ -5,3 +5,6 @@ services: type: gleam@1.5 enableSubdomainAccess: true buildFromGit: https://github.com/zeropsio/recipe-gleam + - hostname: db + type: postgresql@16 + mode: NON_HA diff --git a/zerops.yml b/zerops.yml index f617ec4..fae2650 100644 --- a/zerops.yml +++ b/zerops.yml @@ -8,7 +8,7 @@ zerops: run: base: gleam@1.5 envVariables: - DATABASE_URL: postgresql://${db_user}:${db_password}@${db_hostname}:${db_port} + DATABASE_URL: postgresql://${db_user}:${db_password}@${db_hostname}:${db_port}/${db_dbName} ports: - port: 3000 httpSupport: true From 0b74bc723c17747f22ee6367f6960d2c51f39163 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Mon, 21 Oct 2024 11:39:44 +0530 Subject: [PATCH 07/24] fix db --- src/database.gleam | 61 ++++++++++++++++++++++++++++---------- test/my_web_app_test.gleam | 12 -------- 2 files changed, 46 insertions(+), 27 deletions(-) delete mode 100644 test/my_web_app_test.gleam diff --git a/src/database.gleam b/src/database.gleam index 06ac07e..9769eab 100644 --- a/src/database.gleam +++ b/src/database.gleam @@ -4,25 +4,61 @@ import gleam/io import gleam/dynamic import gleam/int -pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(Int, String) { - io.println("Adding entry and getting count") - // Insert a new entry - let insert_query = "INSERT INTO entries (created_at) VALUES (NOW())" - io.println("Executing insert query: " <> insert_query) +pub fn table(conn: pgo.Connection) -> Result(Nil, String) { + let create_table_query = " + CREATE TABLE IF NOT EXISTS entries ( + id SERIAL PRIMARY KEY, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() + ) + " + io.println("Ensuring entries table exists...") case pgo.execute( - query: insert_query, + query: create_table_query, on: conn, with: [], expecting: dynamic.dynamic ) { Ok(_) -> { - io.println("Entry added successfully") - get_count(conn) + io.println("Table entries created or already exists") + Ok(Nil) } Error(err) -> { let error_message = query_error_to_string(err) - io.println("Failed to add entry: " <> error_message) - Error("Failed to add entry: " <> error_message) + io.println("Failed to create table: " <> error_message) + Error("Failed to create table: " <> error_message) + } + } +} + +pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(Int, String) { + io.println("Adding entry and getting count") + + // First, ensure the table exists + case table(conn) { + Ok(_) -> { + // Table exists or was created, proceed with insert + let insert_query = "INSERT INTO entries (created_at) VALUES (NOW())" + io.println("Executing insert query: " <> insert_query) + case pgo.execute( + query: insert_query, + on: conn, + with: [], + expecting: dynamic.dynamic + ) { + Ok(_) -> { + io.println("Entry added successfully") + get_count(conn) + } + Error(err) -> { + let error_message = query_error_to_string(err) + io.println("Failed to add entry: " <> error_message) + Error("Failed to add entry: " <> error_message) + } + } + } + Error(err) -> { + io.println("Failed to ensure table exists: " <> err) + Error("Failed to ensure table exists: " <> err) } } } @@ -60,19 +96,14 @@ fn query_error_to_string(error: pgo.QueryError) -> String { case error { pgo.ConstraintViolated(message: message, constraint: constraint, detail: detail) -> "Constraint violated: " <> message <> " (Constraint: " <> constraint <> ", Detail: " <> detail <> ")" - pgo.PostgresqlError(code: code, name: name, message: message) -> "PostgreSQL error: " <> message <> " (Code: " <> code <> ", Name: " <> name <> ")" - pgo.UnexpectedArgumentCount(expected: expected, got: got) -> "Unexpected argument count. Expected: " <> int.to_string(expected) <> ", Got: " <> int.to_string(got) - pgo.UnexpectedArgumentType(expected: expected, got: got) -> "Unexpected argument type. Expected: " <> expected <> ", Got: " <> got - pgo.UnexpectedResultType(errors) -> "Unexpected result type: " <> string.inspect(errors) - pgo.ConnectionUnavailable -> "Connection unavailable" } diff --git a/test/my_web_app_test.gleam b/test/my_web_app_test.gleam deleted file mode 100644 index 3831e7a..0000000 --- a/test/my_web_app_test.gleam +++ /dev/null @@ -1,12 +0,0 @@ -import gleeunit -import gleeunit/should - -pub fn main() { - gleeunit.main() -} - -// gleeunit test functions end in `_test` -pub fn hello_world_test() { - 1 - |> should.equal(1) -} From 3c9a65e6bf7213bd4cb55c9529d858b036964f55 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Mon, 21 Oct 2024 13:52:07 +0530 Subject: [PATCH 08/24] ... --- src/database.gleam | 12 +++++++++--- zerops.yml | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/database.gleam b/src/database.gleam index 9769eab..95c4524 100644 --- a/src/database.gleam +++ b/src/database.gleam @@ -1,3 +1,4 @@ +import gleam/result import gleam/string import gleam/pgo import gleam/io @@ -33,10 +34,8 @@ pub fn table(conn: pgo.Connection) -> Result(Nil, String) { pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(Int, String) { io.println("Adding entry and getting count") - // First, ensure the table exists case table(conn) { Ok(_) -> { - // Table exists or was created, proceed with insert let insert_query = "INSERT INTO entries (created_at) VALUES (NOW())" io.println("Executing insert query: " <> insert_query) case pgo.execute( @@ -70,9 +69,16 @@ fn get_count(conn: pgo.Connection) -> Result(Int, String) { query: count_query, on: conn, with: [], - expecting: dynamic.int + expecting: fn(dyn) { + dynamic.tuple2( + dynamic.int, + dynamic.dynamic + )(dyn) + |> result.map(fn(tuple) { tuple.0 }) + } ) { Ok(result) -> { + io.println("Raw result: " <> string.inspect(result)) case result.rows { [count] -> { io.println("Count retrieved successfully: " <> int.to_string(count)) diff --git a/zerops.yml b/zerops.yml index fae2650..8249798 100644 --- a/zerops.yml +++ b/zerops.yml @@ -8,7 +8,7 @@ zerops: run: base: gleam@1.5 envVariables: - DATABASE_URL: postgresql://${db_user}:${db_password}@${db_hostname}:${db_port}/${db_dbName} + DATABASE_URL: ${db_connectionString}/${db_dbName} ports: - port: 3000 httpSupport: true From 78488c4f5bdcb639cf40b0506540f1100b6d6cde Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 22 Oct 2024 16:38:05 +0530 Subject: [PATCH 09/24] Rahhhhhhhhh --- gleam.toml | 1 + manifest.toml | 2 + src/app.gleam | 56 +++++++++++-------- src/database.gleam | 135 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 152 insertions(+), 42 deletions(-) diff --git a/gleam.toml b/gleam.toml index 4c25294..884656b 100644 --- a/gleam.toml +++ b/gleam.toml @@ -19,6 +19,7 @@ gleam_http = ">= 3.6.0 and < 4.0.0" gleam_erlang = ">= 0.25.0 and < 1.0.0" gleam_pgo = ">= 0.14.0 and < 1.0.0" glenvy = ">= 1.0.1 and < 2.0.0" +gluid = ">= 1.0.0 and < 2.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index 2e91f89..e659434 100644 --- a/manifest.toml +++ b/manifest.toml @@ -14,6 +14,7 @@ packages = [ { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, { name = "glenvy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib", "nibble", "simplifile"], otp_app = "glenvy", source = "hex", outer_checksum = "0F95149BF3052724BC2C76DF0235ED12C681683936294C84E2FB7853254FFB00" }, { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" }, + { name = "gluid", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gluid", source = "hex", outer_checksum = "9C0D64DC9A345B7DCC76A2EFDC1F3479E3C6A08A1FB81156E9CCED959F8DEB78" }, { name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" }, { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, @@ -33,4 +34,5 @@ gleam_pgo = { version = ">= 0.14.0 and < 1.0.0" } gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } gleeunit = { version = ">= 1.0.0 and < 2.0.0" } glenvy = { version = ">= 1.0.1 and < 2.0.0" } +gluid = { version = ">= 1.0.0 and < 2.0.0" } mist = { version = ">= 1.2.0 and < 2.0.0" } diff --git a/src/app.gleam b/src/app.gleam index 150c1ee..25f6a56 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -15,52 +15,52 @@ import config pub fn main() { // Load and verify environment variables at startup case dotenv.load() { -Ok(_) -> { - io.println("Environment variables loaded") - case env.get_string("DATABASE_URL") { - Ok(_) -> io.println("DATABASE_URL is set") - Error(_) -> io.println("DATABASE_URL is not set") - } -} + Ok(_) -> { + io.println("Environment variables loaded") + case env.get_string("DATABASE_URL") { + Ok(_) -> io.println("DATABASE_URL is set") + Error(_) -> io.println("DATABASE_URL is not set") + } + } Error(_) -> { io.println("Failed to load environment variables: ") } } - + let port = os.get_env("PORT") |> result.then(int.parse) |> result.unwrap(3000) - + let assert Ok(_) = web_service |> mist.new |> mist.port(port) |> mist.start_http - + ["Started listening on localhost:", int.to_string(port), " ✨"] |> string.concat |> io.println - + process.sleep_forever() } fn web_service(_request) { io.println("Received a new request") - // Check database connection using config - let #(db_status, entry_count) = case config.get_db_connection() { + let #(db_status, entry_count, latest_uuid) = case config.get_db_connection() { Ok(connection) -> { io.println("Database connection successful") // Use the database module to add entry and get count case database.add_entry_and_get_count(connection) { - Ok(count) -> { - io.println("Successfully added entry and got count: " <> int.to_string(count)) - #("Database connection successful! ✅", count) + Ok(#(uuid, count)) -> { + io.println("Successfully added entry with UUID: " <> uuid) + io.println("Total count: " <> int.to_string(count)) + #("Database connection successful! ✅", count, uuid) } Error(err) -> { io.println("Database operation failed: " <> err) - #("Database operation failed: " <> err, 0) + #("Database operation failed: " <> err, 0, "") } } } @@ -69,12 +69,11 @@ fn web_service(_request) { Nil -> "Unknown database connection error" } io.println(error_message) - #(error_message <> " ❌", 0) + #(error_message <> " ❌", 0, "") } } - + io.println("Preparing response") - let body = bytes_builder.from_string( " @@ -106,6 +105,13 @@ fn web_service(_request) { font-weight: bold; margin: 20px 0; } + .uuid { + font-family: monospace; + background-color: #f5f5f5; + padding: 5px 10px; + border-radius: 4px; + margin: 10px 0; + } @@ -116,15 +122,19 @@ fn web_service(_request) { } <> "\"> " <> db_status <> "
+ " <> case string.length(latest_uuid) > 0 { + True -> "
Latest UUID: " <> latest_uuid <> "
" + False -> "" + } <> "
Total entries: " <> int.to_string(entry_count) <> "
-

This is a simple, basic Gleam mist application running on Zerops.io, each request adds an entry to the PostgreSQL database and returns a count.

+

This is a simple, basic Gleam mist application running on Zerops.io, each request adds an entry with a UUID to the PostgreSQL database and returns a count.

See the source repository for more information.

" ) - + io.println("Sending response") Response(200, [#("Content-Type", "text/html")], mist.Bytes(body)) -} +} \ No newline at end of file diff --git a/src/database.gleam b/src/database.gleam index 95c4524..ab0bdfd 100644 --- a/src/database.gleam +++ b/src/database.gleam @@ -1,18 +1,55 @@ -import gleam/result import gleam/string import gleam/pgo import gleam/io import gleam/dynamic import gleam/int +import gluid pub fn table(conn: pgo.Connection) -> Result(Nil, String) { + let check_table_query = " + SELECT EXISTS ( + SELECT FROM pg_tables + WHERE schemaname = 'public' + AND tablename = 'entries' + ); + " + + case pgo.execute( + query: check_table_query, + on: conn, + with: [], + expecting: fn(row) { + dynamic.element(0, dynamic.bool)(row) + } + ) { + Ok(result) -> { + case result.rows { + [exists] -> { + case exists { + True -> add_uuid_column_if_missing(conn) + False -> create_new_table(conn) + } + } + _ -> Error("Unexpected result checking table existence") + } + } + Error(err) -> { + let error_message = query_error_to_string(err) + Error("Failed to check table existence: " <> error_message) + } + } +} + +fn create_new_table(conn: pgo.Connection) -> Result(Nil, String) { let create_table_query = " - CREATE TABLE IF NOT EXISTS entries ( + CREATE TABLE entries ( id SERIAL PRIMARY KEY, + uuid TEXT NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() - ) + ); " - io.println("Ensuring entries table exists...") + io.println("Creating new entries table...") + case pgo.execute( query: create_table_query, on: conn, @@ -20,33 +57,98 @@ pub fn table(conn: pgo.Connection) -> Result(Nil, String) { expecting: dynamic.dynamic ) { Ok(_) -> { - io.println("Table entries created or already exists") + io.println("Table entries created successfully") Ok(Nil) } Error(err) -> { let error_message = query_error_to_string(err) - io.println("Failed to create table: " <> error_message) Error("Failed to create table: " <> error_message) } } } -pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(Int, String) { - io.println("Adding entry and getting count") +fn add_uuid_column_if_missing(conn: pgo.Connection) -> Result(Nil, String) { + let check_column_query = " + SELECT EXISTS ( + SELECT FROM information_schema.columns + WHERE table_name = 'entries' + AND column_name = 'uuid' + ); + " + case pgo.execute( + query: check_column_query, + on: conn, + with: [], + expecting: fn(row) { + dynamic.element(0, dynamic.bool)(row) + } + ) { + Ok(result) -> { + case result.rows { + [exists] -> { + case exists { + False -> { + let add_column_query = " + ALTER TABLE entries + ADD COLUMN uuid TEXT NOT NULL DEFAULT ''; + " + io.println("Adding uuid column to existing table...") + + case pgo.execute( + query: add_column_query, + on: conn, + with: [], + expecting: dynamic.dynamic + ) { + Ok(_) -> { + io.println("UUID column added successfully") + Ok(Nil) + } + Error(err) -> { + let error_message = query_error_to_string(err) + Error("Failed to add uuid column: " <> error_message) + } + } + } + True -> { + io.println("UUID column already exists") + Ok(Nil) + } + } + } + _ -> Error("Unexpected result checking column existence") + } + } + Error(err) -> { + let error_message = query_error_to_string(err) + Error("Failed to check column existence: " <> error_message) + } + } +} + +pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(#(String, Int), String) { + io.println("Adding entry and getting count") case table(conn) { Ok(_) -> { - let insert_query = "INSERT INTO entries (created_at) VALUES (NOW())" - io.println("Executing insert query: " <> insert_query) + let uuid = gluid.guidv4() + io.println("Generated UUID: " <> uuid) + + let insert_query = "INSERT INTO entries (uuid) VALUES ($1)" + io.println("Executing insert query with UUID") + case pgo.execute( query: insert_query, on: conn, - with: [], + with: [pgo.text(uuid)], expecting: dynamic.dynamic ) { Ok(_) -> { io.println("Entry added successfully") - get_count(conn) + case get_count(conn) { + Ok(count) -> Ok(#(uuid, count)) + Error(err) -> Error(err) + } } Error(err) -> { let error_message = query_error_to_string(err) @@ -69,16 +171,11 @@ fn get_count(conn: pgo.Connection) -> Result(Int, String) { query: count_query, on: conn, with: [], - expecting: fn(dyn) { - dynamic.tuple2( - dynamic.int, - dynamic.dynamic - )(dyn) - |> result.map(fn(tuple) { tuple.0 }) + expecting: fn(row) { + dynamic.element(0, dynamic.int)(row) } ) { Ok(result) -> { - io.println("Raw result: " <> string.inspect(result)) case result.rows { [count] -> { io.println("Count retrieved successfully: " <> int.to_string(count)) From 9c292f0304ddf7c243775a4c277a9c32df91d5d1 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 22 Oct 2024 16:44:06 +0530 Subject: [PATCH 10/24] rahhhhhh --- src/app.gleam | 91 ++++++++++++++++++++++++++++++++++++++++++---- src/database.gleam | 30 +++++++++++++++ 2 files changed, 113 insertions(+), 8 deletions(-) diff --git a/src/app.gleam b/src/app.gleam index 25f6a56..d0ea726 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -1,3 +1,4 @@ +import gleam/list import mist import gleam/erlang/process import gleam/bytes_builder @@ -48,7 +49,7 @@ pub fn main() { fn web_service(_request) { io.println("Received a new request") // Check database connection using config - let #(db_status, entry_count, latest_uuid) = case config.get_db_connection() { + let #(db_status, entry_count, latest_uuid, entries) = case config.get_db_connection() { Ok(connection) -> { io.println("Database connection successful") // Use the database module to add entry and get count @@ -56,11 +57,15 @@ fn web_service(_request) { Ok(#(uuid, count)) -> { io.println("Successfully added entry with UUID: " <> uuid) io.println("Total count: " <> int.to_string(count)) - #("Database connection successful! ✅", count, uuid) + + case database.get_all_entries(connection) { + Ok(entries) -> #("Database connection successful! ✅", count, uuid, entries) + Error(_) -> #("Database connection successful! ✅", count, uuid, []) + } } Error(err) -> { io.println("Database operation failed: " <> err) - #("Database operation failed: " <> err, 0, "") + #("Database operation failed: " <> err, 0, "", []) } } } @@ -69,7 +74,7 @@ fn web_service(_request) { Nil -> "Unknown database connection error" } io.println(error_message) - #(error_message <> " ❌", 0, "") + #(error_message <> " ❌", 0, "", []) } } @@ -112,6 +117,40 @@ fn web_service(_request) { border-radius: 4px; margin: 10px 0; } + .latest-entry { + border: 2px solid #4CAF50; + padding: 10px; + margin: 20px 0; + border-radius: 4px; + } + .entries-table { + width: 100%; + border-collapse: collapse; + margin: 20px 0; + background-color: #fff; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + } + .entries-table th, + .entries-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #ddd; + } + .entries-table th { + background-color: #f8f9fa; + font-weight: bold; + } + .entries-table tr:hover { + background-color: #f5f5f5; + } + .new-entry { + background-color: #e8f5e9; + animation: highlight 2s ease-out; + } + @keyframes highlight { + 0% { background-color: #b9f6ca; } + 100% { background-color: #e8f5e9; } + } @@ -122,13 +161,49 @@ fn web_service(_request) { } <> "\"> " <> db_status <> " - " <> case string.length(latest_uuid) > 0 { - True -> "
Latest UUID: " <> latest_uuid <> "
" - False -> "" - } <> " +
Total entries: " <> int.to_string(entry_count) <> "
+ + " <> case string.length(latest_uuid) > 0 { + True -> " +
+

Latest Entry

+
" <> latest_uuid <> "
+
" + False -> "" + } <> " + + " <> case entries { + [] -> "" + entries -> " +

Recent Entries

+ + + + + + + + + " <> string.join( + list.map(entries, fn(entry) { + let #(uuid, timestamp) = entry + " case uuid == latest_uuid { + True -> "new-entry" + False -> "" + } <> "\"> + + + " + }), + "" + ) <> " + +
UUIDCreated At
" <> uuid <> "" <> timestamp <> "
" + } <> " +

This is a simple, basic Gleam mist application running on Zerops.io, each request adds an entry with a UUID to the PostgreSQL database and returns a count.

See the source repository for more information.

diff --git a/src/database.gleam b/src/database.gleam index ab0bdfd..733dc0d 100644 --- a/src/database.gleam +++ b/src/database.gleam @@ -127,6 +127,36 @@ fn add_uuid_column_if_missing(conn: pgo.Connection) -> Result(Nil, String) { } } +pub fn get_all_entries(conn: pgo.Connection) -> Result(List(#(String, String)), String) { + let query = " + SELECT uuid, created_at::text + FROM entries + ORDER BY created_at DESC + LIMIT 5" + + case pgo.execute( + query: query, + on: conn, + with: [], + expecting: fn(row) -> Result(#(String, String), List(dynamic.DecodeError)) { + case + dynamic.tuple2( + dynamic.string, + dynamic.string + )(row) { + Ok(#(uuid, timestamp)) -> Ok(#(uuid, timestamp)) + Error(errs) -> Error(errs) + } + } + ) { + Ok(result) -> Ok(result.rows) + Error(err) -> { + let error_message = query_error_to_string(err) + Error("Failed to get entries: " <> error_message) + } + } +} + pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(#(String, Int), String) { io.println("Adding entry and getting count") case table(conn) { From 9fdcc74630cb3fee8d3e415c5ba6e6cdcc3bfaf6 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 22 Oct 2024 16:50:49 +0530 Subject: [PATCH 11/24] done --- src/app.gleam | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/app.gleam b/src/app.gleam index d0ea726..86bacc7 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -171,11 +171,7 @@ fn web_service(_request) {

Latest Entry

" <> latest_uuid <> "
-
" - False -> "" - } <> " - - " <> case entries { + " <> case entries { [] -> "" entries -> "

Recent Entries

@@ -203,6 +199,9 @@ fn web_service(_request) { " } <> " + " + False -> "" + } <> "

This is a simple, basic Gleam mist application running on Zerops.io, each request adds an entry with a UUID to the PostgreSQL database and returns a count.

See the source repository for more information.

From dd501e3198e306c8e6548108a8f234ea1f8606ac Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 22 Oct 2024 19:33:07 +0530 Subject: [PATCH 12/24] testing --- gleam.toml | 1 + manifest.toml | 2 + src/app.gleam | 238 +++++++++------------------------------------ src/database.gleam | 171 ++++---------------------------- 4 files changed, 70 insertions(+), 342 deletions(-) diff --git a/gleam.toml b/gleam.toml index 884656b..1b7393a 100644 --- a/gleam.toml +++ b/gleam.toml @@ -20,6 +20,7 @@ gleam_erlang = ">= 0.25.0 and < 1.0.0" gleam_pgo = ">= 0.14.0 and < 1.0.0" glenvy = ">= 1.0.1 and < 2.0.0" gluid = ">= 1.0.0 and < 2.0.0" +gleam_json = ">= 2.0.0 and < 3.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index e659434..c1ea1c8 100644 --- a/manifest.toml +++ b/manifest.toml @@ -8,6 +8,7 @@ packages = [ { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, + { name = "gleam_json", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB10B0E7BF44282FB25162F1A24C1A025F6B93E777CCF238C4017E4EEF2CDE97" }, { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, { name = "gleam_pgo", version = "0.14.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "pgo"], otp_app = "gleam_pgo", source = "hex", outer_checksum = "12256A7F351E994A6E43AB67276DD8AACE752991C013F48A288C4A848F3A9758" }, { name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" }, @@ -30,6 +31,7 @@ packages = [ [requirements] gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" } gleam_http = { version = ">= 3.6.0 and < 4.0.0" } +gleam_json = { version = ">= 2.0.0 and < 3.0.0" } gleam_pgo = { version = ">= 0.14.0 and < 1.0.0" } gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } gleeunit = { version = ">= 1.0.0 and < 2.0.0" } diff --git a/src/app.gleam b/src/app.gleam index 86bacc7..0ebb51d 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -1,214 +1,70 @@ -import gleam/list import mist -import gleam/erlang/process -import gleam/bytes_builder -import gleam/http/response.{Response} -import gleam/string import gleam/io import gleam/int -import gleam/erlang/os -import gleam/result -import glenvy/dotenv -import glenvy/env -import database +import gleam/bytes_builder +import gleam/erlang/process +import gleam/json +import gleam/http/response.{Response} import config +import database pub fn main() { - // Load and verify environment variables at startup - case dotenv.load() { - Ok(_) -> { - io.println("Environment variables loaded") - case env.get_string("DATABASE_URL") { - Ok(_) -> io.println("DATABASE_URL is set") - Error(_) -> io.println("DATABASE_URL is not set") - } - } - Error(_) -> { - io.println("Failed to load environment variables: ") - } - } - - let port = - os.get_env("PORT") - |> result.then(int.parse) - |> result.unwrap(3000) + let port = 3000 let assert Ok(_) = - web_service + json_service |> mist.new |> mist.port(port) |> mist.start_http - ["Started listening on localhost:", int.to_string(port), " ✨"] - |> string.concat - |> io.println - + io.println("Server running at: http://localhost:" <> int.to_string(port)) process.sleep_forever() } -fn web_service(_request) { - io.println("Received a new request") - // Check database connection using config - let #(db_status, entry_count, latest_uuid, entries) = case config.get_db_connection() { - Ok(connection) -> { - io.println("Database connection successful") - // Use the database module to add entry and get count - case database.add_entry_and_get_count(connection) { +fn json_service(_) { + case config.get_db_connection() { + Ok(conn) -> { + case database.add_entry_and_get_count(conn) { Ok(#(uuid, count)) -> { - io.println("Successfully added entry with UUID: " <> uuid) - io.println("Total count: " <> int.to_string(count)) - - case database.get_all_entries(connection) { - Ok(entries) -> #("Database connection successful! ✅", count, uuid, entries) - Error(_) -> #("Database connection successful! ✅", count, uuid, []) - } + let message = "This is a simple, basic Gleam application running on Zerops.io, + each request adds an entry to the PostgreSQL database and returns a count. + See the source repository (https://github.com/nermalcat69/gleam-mist-pgo) for more information." + + Response( + 200, + [#("content-type", "application/json")], + mist.Bytes(bytes_builder.from_string( + json.to_string(json.object([ + #("message", json.string(message)), + #("newEntry", json.string(uuid)), + #("count", json.string(int.to_string(count))) + ])) + )) + ) } - Error(err) -> { - io.println("Database operation failed: " <> err) - #("Database operation failed: " <> err, 0, "", []) + Error(_) -> { + Response( + 500, + [#("content-type", "application/json")], + mist.Bytes(bytes_builder.from_string( + json.to_string(json.object([ + #("error", json.string("Database operation failed")) + ])) + )) + ) } } } - Error(err) -> { - let error_message = case err { - Nil -> "Unknown database connection error" - } - io.println(error_message) - #(error_message <> " ❌", 0, "", []) + Error(_) -> { + Response( + 500, + [#("content-type", "application/json")], + mist.Bytes(bytes_builder.from_string( + json.to_string(json.object([ + #("error", json.string("Database connection failed")) + ])) + )) + ) } } - - io.println("Preparing response") - let body = bytes_builder.from_string( - " - - - - Gleam Mist Application on Zerops - - - -

Gleam Mist Application

-
case string.contains(db_status, "successful") { - True -> "success" - False -> "error" - } <> "\"> - " <> db_status <> " -
- -
- Total entries: " <> int.to_string(entry_count) <> " -
- - " <> case string.length(latest_uuid) > 0 { - True -> " -
-

Latest Entry

-
" <> latest_uuid <> "
- " <> case entries { - [] -> "" - entries -> " -

Recent Entries

- - - - - - - - - " <> string.join( - list.map(entries, fn(entry) { - let #(uuid, timestamp) = entry - " case uuid == latest_uuid { - True -> "new-entry" - False -> "" - } <> "\"> - - - " - }), - "" - ) <> " - -
UUIDCreated At
" <> uuid <> "" <> timestamp <> "
" - } <> " -
" - False -> "" - } <> " - -

This is a simple, basic Gleam mist application running on Zerops.io, each request adds an entry with a UUID to the PostgreSQL database and returns a count.

-

See the source repository for more information.

- - " - ) - - io.println("Sending response") - Response(200, [#("Content-Type", "text/html")], mist.Bytes(body)) } \ No newline at end of file diff --git a/src/database.gleam b/src/database.gleam index 733dc0d..0803276 100644 --- a/src/database.gleam +++ b/src/database.gleam @@ -5,124 +5,31 @@ import gleam/dynamic import gleam/int import gluid -pub fn table(conn: pgo.Connection) -> Result(Nil, String) { - let check_table_query = " - SELECT EXISTS ( - SELECT FROM pg_tables - WHERE schemaname = 'public' - AND tablename = 'entries' - ); - " +pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(#(String, Int), String) { + io.println("Adding entry and getting count") + let uuid = gluid.guidv4() + io.println("Generated UUID: " <> uuid) - case pgo.execute( - query: check_table_query, - on: conn, - with: [], - expecting: fn(row) { - dynamic.element(0, dynamic.bool)(row) - } - ) { - Ok(result) -> { - case result.rows { - [exists] -> { - case exists { - True -> add_uuid_column_if_missing(conn) - False -> create_new_table(conn) - } - } - _ -> Error("Unexpected result checking table existence") - } - } - Error(err) -> { - let error_message = query_error_to_string(err) - Error("Failed to check table existence: " <> error_message) - } - } -} - -fn create_new_table(conn: pgo.Connection) -> Result(Nil, String) { - let create_table_query = " - CREATE TABLE entries ( - id SERIAL PRIMARY KEY, - uuid TEXT NOT NULL, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() - ); - " - io.println("Creating new entries table...") + let insert_query = "INSERT INTO entries (uuid) VALUES ($1)" + io.println("Executing insert query with UUID") case pgo.execute( - query: create_table_query, + query: insert_query, on: conn, - with: [], + with: [pgo.text(uuid)], expecting: dynamic.dynamic ) { Ok(_) -> { - io.println("Table entries created successfully") - Ok(Nil) - } - Error(err) -> { - let error_message = query_error_to_string(err) - Error("Failed to create table: " <> error_message) - } - } -} - -fn add_uuid_column_if_missing(conn: pgo.Connection) -> Result(Nil, String) { - let check_column_query = " - SELECT EXISTS ( - SELECT FROM information_schema.columns - WHERE table_name = 'entries' - AND column_name = 'uuid' - ); - " - - case pgo.execute( - query: check_column_query, - on: conn, - with: [], - expecting: fn(row) { - dynamic.element(0, dynamic.bool)(row) - } - ) { - Ok(result) -> { - case result.rows { - [exists] -> { - case exists { - False -> { - let add_column_query = " - ALTER TABLE entries - ADD COLUMN uuid TEXT NOT NULL DEFAULT ''; - " - io.println("Adding uuid column to existing table...") - - case pgo.execute( - query: add_column_query, - on: conn, - with: [], - expecting: dynamic.dynamic - ) { - Ok(_) -> { - io.println("UUID column added successfully") - Ok(Nil) - } - Error(err) -> { - let error_message = query_error_to_string(err) - Error("Failed to add uuid column: " <> error_message) - } - } - } - True -> { - io.println("UUID column already exists") - Ok(Nil) - } - } - } - _ -> Error("Unexpected result checking column existence") + io.println("Entry added successfully") + case get_count(conn) { + Ok(count) -> Ok(#(uuid, count)) + Error(err) -> Error(err) } } Error(err) -> { let error_message = query_error_to_string(err) - Error("Failed to check column existence: " <> error_message) + io.println("Failed to add entry: " <> error_message) + Error("Failed to add entry: " <> error_message) } } } @@ -139,13 +46,12 @@ pub fn get_all_entries(conn: pgo.Connection) -> Result(List(#(String, String)), on: conn, with: [], expecting: fn(row) -> Result(#(String, String), List(dynamic.DecodeError)) { - case - dynamic.tuple2( - dynamic.string, - dynamic.string - )(row) { - Ok(#(uuid, timestamp)) -> Ok(#(uuid, timestamp)) - Error(errs) -> Error(errs) + case dynamic.tuple2( + dynamic.string, + dynamic.string + )(row) { + Ok(#(uuid, timestamp)) -> Ok(#(uuid, timestamp)) + Error(errs) -> Error(errs) } } ) { @@ -157,43 +63,6 @@ pub fn get_all_entries(conn: pgo.Connection) -> Result(List(#(String, String)), } } -pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(#(String, Int), String) { - io.println("Adding entry and getting count") - case table(conn) { - Ok(_) -> { - let uuid = gluid.guidv4() - io.println("Generated UUID: " <> uuid) - - let insert_query = "INSERT INTO entries (uuid) VALUES ($1)" - io.println("Executing insert query with UUID") - - case pgo.execute( - query: insert_query, - on: conn, - with: [pgo.text(uuid)], - expecting: dynamic.dynamic - ) { - Ok(_) -> { - io.println("Entry added successfully") - case get_count(conn) { - Ok(count) -> Ok(#(uuid, count)) - Error(err) -> Error(err) - } - } - Error(err) -> { - let error_message = query_error_to_string(err) - io.println("Failed to add entry: " <> error_message) - Error("Failed to add entry: " <> error_message) - } - } - } - Error(err) -> { - io.println("Failed to ensure table exists: " <> err) - Error("Failed to ensure table exists: " <> err) - } - } -} - fn get_count(conn: pgo.Connection) -> Result(Int, String) { let count_query = "SELECT COUNT(*) FROM entries" io.println("Executing count query: " <> count_query) From 7562bcae37f330cb3afb4f4cb4abe10f98c3e7be Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Thu, 24 Oct 2024 01:30:18 +0530 Subject: [PATCH 13/24] coughs errors --- gleam.toml | 4 +- manifest.toml | 17 +++---- src/app.gleam | 110 ++++++++++++++++++++++----------------------- src/database.gleam | 21 +++++---- 4 files changed, 78 insertions(+), 74 deletions(-) diff --git a/gleam.toml b/gleam.toml index 1b7393a..f3d8466 100644 --- a/gleam.toml +++ b/gleam.toml @@ -14,13 +14,13 @@ version = "1.0.0" [dependencies] gleam_stdlib = ">= 0.34.0 and < 2.0.0" -mist = ">= 1.2.0 and < 2.0.0" +mist = ">= 3.0.0 and < 4.0.0" gleam_http = ">= 3.6.0 and < 4.0.0" gleam_erlang = ">= 0.25.0 and < 1.0.0" -gleam_pgo = ">= 0.14.0 and < 1.0.0" glenvy = ">= 1.0.1 and < 2.0.0" gluid = ">= 1.0.0 and < 2.0.0" gleam_json = ">= 2.0.0 and < 3.0.0" +gleam_pgo = ">= 0.14.0 and < 1.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index c1ea1c8..e419960 100644 --- a/manifest.toml +++ b/manifest.toml @@ -5,27 +5,28 @@ packages = [ { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, { name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" }, { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, - { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, - { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, - { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, + { name = "gleam_crypto", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "8AE56026B3E05EBB1F076778478A762E9EB62B31AEEB4285755452F397029D22" }, + { name = "gleam_erlang", version = "0.27.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "DE468F676D71B313C6C8C5334425CFCF827837333F8AB47B64D8A6D7AA40185D" }, + { name = "gleam_http", version = "3.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "EA66440C2269F7CED0F6845E5BD0DB68095775D627FA709A841CA78A398D6D56" }, { name = "gleam_json", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB10B0E7BF44282FB25162F1A24C1A025F6B93E777CCF238C4017E4EEF2CDE97" }, - { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, + { name = "gleam_otp", version = "0.12.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BFACC1513410DF5A1617169A9CD7EA334973AC71D860A17574BA7B2EADD89A6F" }, { name = "gleam_pgo", version = "0.14.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "pgo"], otp_app = "gleam_pgo", source = "hex", outer_checksum = "12256A7F351E994A6E43AB67276DD8AACE752991C013F48A288C4A848F3A9758" }, - { name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" }, + { name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" }, { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, { name = "glenvy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib", "nibble", "simplifile"], otp_app = "glenvy", source = "hex", outer_checksum = "0F95149BF3052724BC2C76DF0235ED12C681683936294C84E2FB7853254FFB00" }, - { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" }, + { name = "glisten", version = "6.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], otp_app = "glisten", source = "hex", outer_checksum = "912132751031473CB38F454120124FFC96AF6B0EA33D92C9C90DB16327A2A972" }, { name = "gluid", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gluid", source = "hex", outer_checksum = "9C0D64DC9A345B7DCC76A2EFDC1F3479E3C6A08A1FB81156E9CCED959F8DEB78" }, { name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" }, { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, - { name = "mist", version = "1.2.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "109B4D64E68C104CC23BB3CC5441ECD479DD7444889DA01113B75C6AF0F0E17B" }, + { name = "mist", version = "3.0.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "CDA1A74E768419235E16886463EC4722EFF4AB3F8D820A76EAD45D7C167D7282" }, { name = "nibble", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "nibble", source = "hex", outer_checksum = "67C6BEBC1AB6D771AB893B4A7B3E66C92668C6E7774C335FEFCD545B06435FE5" }, { name = "opentelemetry_api", version = "1.4.0", build_tools = ["rebar3", "mix"], requirements = [], otp_app = "opentelemetry_api", source = "hex", outer_checksum = "3DFBBFAA2C2ED3121C5C483162836C4F9027DEF469C41578AF5EF32589FCFC58" }, { name = "pg_types", version = "0.4.0", build_tools = ["rebar3"], requirements = [], otp_app = "pg_types", source = "hex", outer_checksum = "B02EFA785CAECECF9702C681C80A9CA12A39F9161A846CE17B01FB20AEEED7EB" }, { name = "pgo", version = "0.14.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "71016C22599936E042DC0012EE4589D24C71427D266292F775EBF201D97DF9C9" }, { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, { name = "simplifile", version = "2.2.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0DFABEF7DC7A9E2FF4BB27B108034E60C81BEBFCB7AB816B9E7E18ED4503ACD8" }, + { name = "telemetry", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "7015FC8919DBE63764F4B4B87A95B7C0996BD539E0D499BE6EC9D7F3875B79E6" }, ] [requirements] @@ -37,4 +38,4 @@ gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } gleeunit = { version = ">= 1.0.0 and < 2.0.0" } glenvy = { version = ">= 1.0.1 and < 2.0.0" } gluid = { version = ">= 1.0.0 and < 2.0.0" } -mist = { version = ">= 1.2.0 and < 2.0.0" } +mist = { version = ">= 3.0.0 and < 4.0.0" } diff --git a/src/app.gleam b/src/app.gleam index 0ebb51d..4a78f6c 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -1,70 +1,68 @@ -import mist -import gleam/io -import gleam/int +import gleam/http.{Get} +import gleam/http/response.{type Response} +import gleam/http/request.{type Request} import gleam/bytes_builder -import gleam/erlang/process +import gleam/io import gleam/json -import gleam/http/response.{Response} -import config +import gleam/pgo +import gleam/erlang/process import database +import config +import mist.{type Connection, type ResponseData} pub fn main() { + case config.get_db_connection() { + Ok(db) -> { + let assert Ok(_) = + mist.new(my_service(db)) + |> mist.port(3000) + |> mist.start_http - let port = 3000 - let assert Ok(_) = - json_service - |> mist.new - |> mist.port(port) - |> mist.start_http - - io.println("Server running at: http://localhost:" <> int.to_string(port)) - process.sleep_forever() + io.println("Server started on port 3000") + process.sleep_forever() + } + Error(Nil) -> { + io.println("Failed to initialize database connection") + process.sleep_forever() + } + } } -fn json_service(_) { - case config.get_db_connection() { - Ok(conn) -> { - case database.add_entry_and_get_count(conn) { - Ok(#(uuid, count)) -> { - let message = "This is a simple, basic Gleam application running on Zerops.io, - each request adds an entry to the PostgreSQL database and returns a count. - See the source repository (https://github.com/nermalcat69/gleam-mist-pgo) for more information." +fn my_service( + db: pgo.Connection, +) -> fn(Request(Connection)) -> Response(ResponseData) { + fn(request: Request(Connection)) -> Response(ResponseData) { + case request.method { + Get -> handle_get_request(db) + _ -> json_response(404, json.object([ + #("error", json.string("Method not allowed")) + ])) + } + } +} - Response( - 200, - [#("content-type", "application/json")], - mist.Bytes(bytes_builder.from_string( - json.to_string(json.object([ - #("message", json.string(message)), - #("newEntry", json.string(uuid)), - #("count", json.string(int.to_string(count))) - ])) - )) - ) - } - Error(_) -> { - Response( - 500, - [#("content-type", "application/json")], - mist.Bytes(bytes_builder.from_string( - json.to_string(json.object([ - #("error", json.string("Database operation failed")) - ])) - )) - ) - } - } +fn handle_get_request(db: pgo.Connection) -> Response(ResponseData) { + case database.add_entry_and_get_count(db) { + Ok(#(uuid, count)) -> { + let response_body = json.object([ + #("uuid", json.string(uuid)), + #("count", json.int(count)), + ]) + json_response(200, response_body) } Error(_) -> { - Response( - 500, - [#("content-type", "application/json")], - mist.Bytes(bytes_builder.from_string( - json.to_string(json.object([ - #("error", json.string("Database connection failed")) - ])) - )) - ) + json_response(500, json.object([ + #("error", json.string("Database error")) + ])) } } +} + +fn json_response( + status: Int, + body: json.Json, +) -> Response(ResponseData) { + response.new(status) + |> response.set_header("content-type", "application/json") + |> response.set_body(bytes_builder.from_string(json.to_string(body))) } \ No newline at end of file diff --git a/src/database.gleam b/src/database.gleam index 0803276..66adb3a 100644 --- a/src/database.gleam +++ b/src/database.gleam @@ -34,13 +34,13 @@ pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(#(String, Int), S } } -pub fn get_all_entries(conn: pgo.Connection) -> Result(List(#(String, String)), String) { +pub fn get_latest_entry(conn: pgo.Connection) -> Result(#(String, String), String) { let query = " - SELECT uuid, created_at::text - FROM entries - ORDER BY created_at DESC - LIMIT 5" - + SELECT uuid, created_at::text + FROM entries + ORDER BY created_at DESC + LIMIT 1" + case pgo.execute( query: query, on: conn, @@ -55,10 +55,15 @@ pub fn get_all_entries(conn: pgo.Connection) -> Result(List(#(String, String)), } } ) { - Ok(result) -> Ok(result.rows) + Ok(result) -> { + case result.rows { + [entry, ..] -> Ok(entry) + [] -> Error("No entries found") + } + } Error(err) -> { let error_message = query_error_to_string(err) - Error("Failed to get entries: " <> error_message) + Error("Failed to get latest entry: " <> error_message) } } } From 136dcec4556408a4167fb4b00b174aefc4e1a01b Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Thu, 24 Oct 2024 16:39:23 +0530 Subject: [PATCH 14/24] wisp --- gleam.toml | 1 + manifest.toml | 7 +++ src/app.gleam | 117 ++++++++++++++++++++++++++++---------------------- 3 files changed, 74 insertions(+), 51 deletions(-) diff --git a/gleam.toml b/gleam.toml index f3d8466..1e85f2a 100644 --- a/gleam.toml +++ b/gleam.toml @@ -21,6 +21,7 @@ glenvy = ">= 1.0.1 and < 2.0.0" gluid = ">= 1.0.0 and < 2.0.0" gleam_json = ">= 2.0.0 and < 3.0.0" gleam_pgo = ">= 0.14.0 and < 1.0.0" +wisp = ">= 1.2.0 and < 2.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index e419960..8d52250 100644 --- a/manifest.toml +++ b/manifest.toml @@ -4,6 +4,9 @@ packages = [ { name = "backoff", version = "1.1.6", build_tools = ["rebar3"], requirements = [], otp_app = "backoff", source = "hex", outer_checksum = "CF0CFFF8995FB20562F822E5CC47D8CCF664C5ECDC26A684CBE85C225F9D7C39" }, { name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" }, + { name = "directories", version = "1.1.0", build_tools = ["gleam"], requirements = ["envoy", "gleam_stdlib", "platform", "simplifile"], otp_app = "directories", source = "hex", outer_checksum = "BDA521A4EB9EE3A7894F0DC863797878E91FF5C7826F7084B2E731E208BDB076" }, + { name = "envoy", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "95FD059345AA982E89A0B6E2A3BF1CF43E17A7048DCD85B5B65D3B9E4E39D359" }, + { name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" }, { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, { name = "gleam_crypto", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "8AE56026B3E05EBB1F076778478A762E9EB62B31AEEB4285755452F397029D22" }, { name = "gleam_erlang", version = "0.27.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "DE468F676D71B313C6C8C5334425CFCF827837333F8AB47B64D8A6D7AA40185D" }, @@ -19,14 +22,17 @@ packages = [ { name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" }, { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" }, + { name = "marceau", version = "1.3.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "2D1C27504BEF45005F5DFB18591F8610FB4BFA91744878210BDC464412EC44E9" }, { name = "mist", version = "3.0.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "CDA1A74E768419235E16886463EC4722EFF4AB3F8D820A76EAD45D7C167D7282" }, { name = "nibble", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "nibble", source = "hex", outer_checksum = "67C6BEBC1AB6D771AB893B4A7B3E66C92668C6E7774C335FEFCD545B06435FE5" }, { name = "opentelemetry_api", version = "1.4.0", build_tools = ["rebar3", "mix"], requirements = [], otp_app = "opentelemetry_api", source = "hex", outer_checksum = "3DFBBFAA2C2ED3121C5C483162836C4F9027DEF469C41578AF5EF32589FCFC58" }, { name = "pg_types", version = "0.4.0", build_tools = ["rebar3"], requirements = [], otp_app = "pg_types", source = "hex", outer_checksum = "B02EFA785CAECECF9702C681C80A9CA12A39F9161A846CE17B01FB20AEEED7EB" }, { name = "pgo", version = "0.14.0", build_tools = ["rebar3"], requirements = ["backoff", "opentelemetry_api", "pg_types"], otp_app = "pgo", source = "hex", outer_checksum = "71016C22599936E042DC0012EE4589D24C71427D266292F775EBF201D97DF9C9" }, + { name = "platform", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "platform", source = "hex", outer_checksum = "8339420A95AD89AAC0F82F4C3DB8DD401041742D6C3F46132A8739F6AEB75391" }, { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, { name = "simplifile", version = "2.2.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0DFABEF7DC7A9E2FF4BB27B108034E60C81BEBFCB7AB816B9E7E18ED4503ACD8" }, { name = "telemetry", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "7015FC8919DBE63764F4B4B87A95B7C0996BD539E0D499BE6EC9D7F3875B79E6" }, + { name = "wisp", version = "1.2.0", build_tools = ["gleam"], requirements = ["directories", "exception", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_json", "gleam_stdlib", "logging", "marceau", "mist", "simplifile"], otp_app = "wisp", source = "hex", outer_checksum = "F71265D2F1DE11426535A2FA1DA3B11D2FFB783B116DF9496BC8C41983EBADB4" }, ] [requirements] @@ -39,3 +45,4 @@ gleeunit = { version = ">= 1.0.0 and < 2.0.0" } glenvy = { version = ">= 1.0.1 and < 2.0.0" } gluid = { version = ">= 1.0.0 and < 2.0.0" } mist = { version = ">= 3.0.0 and < 4.0.0" } +wisp = { version = ">= 1.2.0 and < 2.0.0" } diff --git a/src/app.gleam b/src/app.gleam index 4a78f6c..de61dfe 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -1,68 +1,83 @@ import gleam/http.{Get} -import gleam/http/response.{type Response} -import gleam/http/request.{type Request} -import gleam/bytes_builder -import gleam/io import gleam/json +import gleam/io +import wisp import gleam/pgo -import gleam/erlang/process import database import config -import mist.{type Connection, type ResponseData} +import mist +import gleam/erlang/process -pub fn main() { - case config.get_db_connection() { - Ok(db) -> { - let assert Ok(_) = - mist.new(my_service(db)) - |> mist.port(3000) - |> mist.start_http +pub type Request = wisp.Request - io.println("Server started on port 3000") - process.sleep_forever() - } - Error(Nil) -> { - io.println("Failed to initialize database connection") - process.sleep_forever() - } - } +pub type Response = wisp.Response + +pub fn main() { + wisp.configure_logger() + + let assert Ok(conn) = config.get_db_connection() + + let app = wisp.init() + |> wisp.handle_request(fn(req) { handle_request(req, conn) }) + + let secret_key_base = wisp.random_string(64) + + let assert Ok(_) = + mist.handler(app, secret_key_base) + |> mist.new + |> mist.port(3000) + |> mist.start_http + + io.println("Starting server on port 3000") + process.sleep_forever() } -fn my_service( - db: pgo.Connection, -) -> fn(Request(Connection)) -> Response(ResponseData) { - fn(request: Request(Connection)) -> Response(ResponseData) { - case request.method { - Get -> handle_get_request(db) - _ -> json_response(404, json.object([ - #("error", json.string("Method not allowed")) - ])) - } +pub fn handle_request(req: Request, conn: pgo.Connection) -> Response { + case req.method, wisp.path_segments(req) { + Get, [] -> handle_root(conn) + Get, ["status"] -> handle_status() + _, _ -> wisp.not_found() } } -fn handle_get_request(db: pgo.Connection) -> Response(ResponseData) { - case database.add_entry_and_get_count(db) { +fn handle_root(conn: pgo.Connection) -> Response { + case database.add_entry_and_get_count(conn) { Ok(#(uuid, count)) -> { - let response_body = json.object([ - #("uuid", json.string(uuid)), - #("count", json.int(count)), - ]) - json_response(200, response_body) - } - Error(_) -> { - json_response(500, json.object([ - #("error", json.string("Database error")) - ])) + case database.get_latest_entry(conn) { + Ok(#(latest_uuid, timestamp)) -> { + let body = json.object([ + #("message", json.string("This is a simple, basic Gleam / Wisp application running on Zerops.io, each request adds an entry to the PostgreSQL database and returns a count.")), + #("newEntry", json.string(uuid)), + #("latestEntry", json.object([ + #("uuid", json.string(latest_uuid)), + #("timestamp", json.string(timestamp)) + ])), + #("count", json.int(count)) + ]) + wisp.json_response(json.to_string_builder(body), 201) + } + Error(err) -> wisp.json_response( + json.to_string_builder(json.object([ + #("error", json.string(err)) + ])), + 500 + ) + } } + Error(err) -> wisp.json_response( + json.to_string_builder(json.object([ + #("error", json.string(err)) + ])), + 500 + ) } } -fn json_response( - status: Int, - body: json.Json, -) -> Response(ResponseData) { - response.new(status) - |> response.set_header("content-type", "application/json") - |> response.set_body(bytes_builder.from_string(json.to_string(body))) -} \ No newline at end of file +fn handle_status() -> Response { + wisp.json_response( + json.to_string_builder(json.object([ + #("status", json.string("UP")) + ])), + 200 + ) +} From b4dad6c7e4ed31d41cf28dd36141c008f8fb5b37 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Thu, 24 Oct 2024 16:45:57 +0530 Subject: [PATCH 15/24] Update app.gleam --- src/app.gleam | 53 ++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/app.gleam b/src/app.gleam index de61dfe..9ffc784 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -1,33 +1,32 @@ import gleam/http.{Get} import gleam/json import gleam/io -import wisp +import wisp.{type Request, type Response, not_found} +import wisp/wisp_mist import gleam/pgo import database import config import mist import gleam/erlang/process -pub type Request = wisp.Request - -pub type Response = wisp.Response - pub fn main() { + // Configure logging wisp.configure_logger() let assert Ok(conn) = config.get_db_connection() - let app = wisp.init() - |> wisp.handle_request(fn(req) { handle_request(req, conn) }) - + // Generate a secure key for the server let secret_key_base = wisp.random_string(64) + // Create and start the server let assert Ok(_) = - mist.handler(app, secret_key_base) + // Create handler function that captures the database connection + {fn(req) { handle_request(req, conn) }} + |> wisp_mist.handler(secret_key_base) |> mist.new |> mist.port(3000) |> mist.start_http - + io.println("Starting server on port 3000") process.sleep_forever() } @@ -36,7 +35,7 @@ pub fn handle_request(req: Request, conn: pgo.Connection) -> Response { case req.method, wisp.path_segments(req) { Get, [] -> handle_root(conn) Get, ["status"] -> handle_status() - _, _ -> wisp.not_found() + _, _ -> not_found() } } @@ -54,22 +53,28 @@ fn handle_root(conn: pgo.Connection) -> Response { ])), #("count", json.int(count)) ]) - wisp.json_response(json.to_string_builder(body), 201) + + wisp.json_response( + json.to_string_builder(body), + 201 + ) } - Error(err) -> wisp.json_response( - json.to_string_builder(json.object([ - #("error", json.string(err)) - ])), - 500 - ) + Error(err) -> + wisp.json_response( + json.to_string_builder(json.object([ + #("error", json.string(err)) + ])), + 500 + ) } } - Error(err) -> wisp.json_response( - json.to_string_builder(json.object([ - #("error", json.string(err)) - ])), - 500 - ) + Error(err) -> + wisp.json_response( + json.to_string_builder(json.object([ + #("error", json.string(err)) + ])), + 500 + ) } } From 1fdadaa5a1b54722a23121e677c79ca8413e8ef7 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Thu, 24 Oct 2024 16:46:19 +0530 Subject: [PATCH 16/24] Update app.gleam --- src/app.gleam | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/app.gleam b/src/app.gleam index 9ffc784..4fcaaf7 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -10,17 +10,13 @@ import mist import gleam/erlang/process pub fn main() { - // Configure logging wisp.configure_logger() let assert Ok(conn) = config.get_db_connection() - // Generate a secure key for the server let secret_key_base = wisp.random_string(64) - // Create and start the server let assert Ok(_) = - // Create handler function that captures the database connection {fn(req) { handle_request(req, conn) }} |> wisp_mist.handler(secret_key_base) |> mist.new From 2a933f76830a72584891efdc51fd231c6d302fae Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 29 Oct 2024 12:57:42 +0530 Subject: [PATCH 17/24] json pg --- src/app.gleam | 159 +++++++++++++++++++++++++++++++++-------------- src/config.gleam | 2 +- 2 files changed, 114 insertions(+), 47 deletions(-) diff --git a/src/app.gleam b/src/app.gleam index 4fcaaf7..cc01ede 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -1,84 +1,151 @@ import gleam/http.{Get} import gleam/json import gleam/io -import wisp.{type Request, type Response, not_found} -import wisp/wisp_mist +import wisp.{type Request, type Response} import gleam/pgo +import gleam/erlang/process import database import config +import gleam/result import mist -import gleam/erlang/process +import wisp/wisp_mist + +pub type AppError { + DatabaseError(String) + ConfigError(String) + ServerError(String) +} + +pub type ApiResponse(a) { + ApiResponse( + data: a, + status: Int, + ) +} + +pub type RootData { + RootData( + message: String, + new_entry: String, + latest_entry: LatestEntry, + count: Int, + ) +} + +pub type LatestEntry { + LatestEntry( + uuid: String, + timestamp: String, + ) +} pub fn main() { wisp.configure_logger() - - let assert Ok(conn) = config.get_db_connection() - let secret_key_base = wisp.random_string(64) + + use db_conn <- result.map(setup_database()) + let handler = fn(req) { handle_request(req, db_conn) } + let assert Ok(_) = - {fn(req) { handle_request(req, conn) }} + handler |> wisp_mist.handler(secret_key_base) |> mist.new |> mist.port(3000) |> mist.start_http - - io.println("Starting server on port 3000") + process.sleep_forever() } +fn setup_database() -> Result(pgo.Connection, AppError) { + use db_conn <- result.try( + config.get_db_connection() + |> result.map_error(fn(_) { ConfigError("Database connection failed") }) + ) + + io.println("✓ Server initialized with database connection") + Ok(db_conn) +} + pub fn handle_request(req: Request, conn: pgo.Connection) -> Response { case req.method, wisp.path_segments(req) { Get, [] -> handle_root(conn) Get, ["status"] -> handle_status() - _, _ -> not_found() + _, _ -> wisp.not_found() } } fn handle_root(conn: pgo.Connection) -> Response { - case database.add_entry_and_get_count(conn) { - Ok(#(uuid, count)) -> { - case database.get_latest_entry(conn) { - Ok(#(latest_uuid, timestamp)) -> { - let body = json.object([ - #("message", json.string("This is a simple, basic Gleam / Wisp application running on Zerops.io, each request adds an entry to the PostgreSQL database and returns a count.")), - #("newEntry", json.string(uuid)), - #("latestEntry", json.object([ - #("uuid", json.string(latest_uuid)), - #("timestamp", json.string(timestamp)) - ])), - #("count", json.int(count)) - ]) - - wisp.json_response( - json.to_string_builder(body), - 201 - ) - } - Error(err) -> - wisp.json_response( - json.to_string_builder(json.object([ - #("error", json.string(err)) - ])), - 500 - ) - } + case process_root_request(conn) { + Ok(ApiResponse(data: data, status: status)) -> { + wisp.json_response( + json.to_string_builder(encode_root_data(data)), + status, + ) } - Error(err) -> + Error(error) -> { + let #(status, message) = error_response(error) wisp.json_response( - json.to_string_builder(json.object([ - #("error", json.string(err)) - ])), - 500 + json.to_string_builder(encode_error(message)), + status, ) + } } } +fn process_root_request(conn: pgo.Connection) -> Result(ApiResponse(RootData), AppError) { + use #(uuid, count) <- result.try( + database.add_entry_and_get_count(conn) + |> result.map_error(DatabaseError) + ) + + use #(latest_uuid, timestamp) <- result.try( + database.get_latest_entry(conn) + |> result.map_error(DatabaseError) + ) + + let data = RootData( + message: "This is a simple, basic Gleam / Wisp application running on Zerops.io, each request adds an entry to the PostgreSQL database and returns a count.", + new_entry: uuid, + latest_entry: LatestEntry(uuid: latest_uuid, timestamp: timestamp), + count: count, + ) + + Ok(ApiResponse(data: data, status: 201)) +} + fn handle_status() -> Response { wisp.json_response( - json.to_string_builder(json.object([ - #("status", json.string("UP")) - ])), - 200 + json.to_string_builder(encode_status_response()), + 200, ) } + +// JSON encoders +fn encode_root_data(data: RootData) -> json.Json { + json.object([ + #("message", json.string(data.message)), + #("newEntry", json.string(data.new_entry)), + #("latestEntry", json.object([ + #("uuid", json.string(data.latest_entry.uuid)), + #("timestamp", json.string(data.latest_entry.timestamp)), + ])), + #("count", json.int(data.count)), + ]) +} + +fn encode_status_response() -> json.Json { + json.object([#("status", json.string("UP"))]) +} + +fn encode_error(message: String) -> json.Json { + json.object([#("error", json.string(message))]) +} + +fn error_response(error: AppError) -> #(Int, String) { + case error { + ConfigError(msg) -> #(500, "Configuration error: " <> msg) + DatabaseError(msg) -> #(500, "Database error: " <> msg) + ServerError(msg) -> #(500, "Server error: " <> msg) + } +} diff --git a/src/config.gleam b/src/config.gleam index d74d3ef..af6c572 100644 --- a/src/config.gleam +++ b/src/config.gleam @@ -28,4 +28,4 @@ pub fn get_db_connection() -> Result(pgo.Connection, Nil) { Error(Nil) } } -} \ No newline at end of file +} From 5c1af00cb0b9cad4df075d9f2043d79faf45e04e Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 29 Oct 2024 14:33:39 +0530 Subject: [PATCH 18/24] rahhhhhhh --- src/app.gleam | 21 +-------------------- src/config.gleam | 3 ++- src/database.gleam | 34 ---------------------------------- 3 files changed, 3 insertions(+), 55 deletions(-) diff --git a/src/app.gleam b/src/app.gleam index cc01ede..11febcd 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -27,18 +27,10 @@ pub type RootData { RootData( message: String, new_entry: String, - latest_entry: LatestEntry, count: Int, ) } -pub type LatestEntry { - LatestEntry( - uuid: String, - timestamp: String, - ) -} - pub fn main() { wisp.configure_logger() let secret_key_base = wisp.random_string(64) @@ -99,15 +91,9 @@ fn process_root_request(conn: pgo.Connection) -> Result(ApiResponse(RootData), A |> result.map_error(DatabaseError) ) - use #(latest_uuid, timestamp) <- result.try( - database.get_latest_entry(conn) - |> result.map_error(DatabaseError) - ) - let data = RootData( - message: "This is a simple, basic Gleam / Wisp application running on Zerops.io, each request adds an entry to the PostgreSQL database and returns a count.", + message: "This is a simple Gleam application running on Zerops.io, each request adds an entry to the PostgreSQL database and returns a count. See the source repository (https://github.com/zeropsio/recipe-nodejs) for more information.", new_entry: uuid, - latest_entry: LatestEntry(uuid: latest_uuid, timestamp: timestamp), count: count, ) @@ -121,15 +107,10 @@ fn handle_status() -> Response { ) } -// JSON encoders fn encode_root_data(data: RootData) -> json.Json { json.object([ #("message", json.string(data.message)), #("newEntry", json.string(data.new_entry)), - #("latestEntry", json.object([ - #("uuid", json.string(data.latest_entry.uuid)), - #("timestamp", json.string(data.latest_entry.timestamp)), - ])), #("count", json.int(data.count)), ]) } diff --git a/src/config.gleam b/src/config.gleam index af6c572..a0408f7 100644 --- a/src/config.gleam +++ b/src/config.gleam @@ -11,6 +11,7 @@ pub fn get_db_connection() -> Result(pgo.Connection, Nil) { case env.get_string("DATABASE_URL") { Ok(database_url) -> { + io.println("Attempting to connect to: " <> database_url) case pgo.url_config(database_url) { Ok(config) -> { let connection = pgo.connect(config) @@ -28,4 +29,4 @@ pub fn get_db_connection() -> Result(pgo.Connection, Nil) { Error(Nil) } } -} +} \ No newline at end of file diff --git a/src/database.gleam b/src/database.gleam index 66adb3a..1582068 100644 --- a/src/database.gleam +++ b/src/database.gleam @@ -34,40 +34,6 @@ pub fn add_entry_and_get_count(conn: pgo.Connection) -> Result(#(String, Int), S } } -pub fn get_latest_entry(conn: pgo.Connection) -> Result(#(String, String), String) { - let query = " - SELECT uuid, created_at::text - FROM entries - ORDER BY created_at DESC - LIMIT 1" - - case pgo.execute( - query: query, - on: conn, - with: [], - expecting: fn(row) -> Result(#(String, String), List(dynamic.DecodeError)) { - case dynamic.tuple2( - dynamic.string, - dynamic.string - )(row) { - Ok(#(uuid, timestamp)) -> Ok(#(uuid, timestamp)) - Error(errs) -> Error(errs) - } - } - ) { - Ok(result) -> { - case result.rows { - [entry, ..] -> Ok(entry) - [] -> Error("No entries found") - } - } - Error(err) -> { - let error_message = query_error_to_string(err) - Error("Failed to get latest entry: " <> error_message) - } - } -} - fn get_count(conn: pgo.Connection) -> Result(Int, String) { let count_query = "SELECT COUNT(*) FROM entries" io.println("Executing count query: " <> count_query) From b1e9267db18b1fc0835ff70c3e992b5496835d58 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 29 Oct 2024 14:42:20 +0530 Subject: [PATCH 19/24] Create build.yml --- .github/workflows/build.yml | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ad0277c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,41 @@ +name: Build and Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + name: Build and test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Erlang + uses: erlef/setup-beam@v1 + with: + otp-version: '25' + gleam-version: '0.34.1' + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + _build + deps + key: ${{ runner.os }}-mix-${{ hashFiles('**/gleam.toml') }} + restore-keys: | + ${{ runner.os }}-mix- + + - name: Install dependencies + run: gleam deps download + + - name: Run build + run: gleam run build + + - name: Run tests + run: gleam test + \ No newline at end of file From e7d22fa0bcac2b6e3c667e8d7f429ba94ba64d0f Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 29 Oct 2024 14:49:15 +0530 Subject: [PATCH 20/24] organize it --- src/app.gleam | 4 ++-- src/{ => db}/config.gleam | 0 src/{ => db}/database.gleam | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{ => db}/config.gleam (100%) rename src/{ => db}/database.gleam (100%) diff --git a/src/app.gleam b/src/app.gleam index 11febcd..3770d7e 100644 --- a/src/app.gleam +++ b/src/app.gleam @@ -4,8 +4,8 @@ import gleam/io import wisp.{type Request, type Response} import gleam/pgo import gleam/erlang/process -import database -import config +import db/database +import db/config import gleam/result import mist import wisp/wisp_mist diff --git a/src/config.gleam b/src/db/config.gleam similarity index 100% rename from src/config.gleam rename to src/db/config.gleam diff --git a/src/database.gleam b/src/db/database.gleam similarity index 100% rename from src/database.gleam rename to src/db/database.gleam From 83a7a6f1dd4dbba21510a253f904cf4de15c7449 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 29 Oct 2024 14:50:24 +0530 Subject: [PATCH 21/24] Update build.yml --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ad0277c..d1720b2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,7 @@ jobs: with: otp-version: '25' gleam-version: '0.34.1' + rebar3-version: '3.22.1' - name: Cache dependencies uses: actions/cache@v3 From 437a5dd1e760b44f4834c72609825cbe0cd47bc2 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 29 Oct 2024 14:52:59 +0530 Subject: [PATCH 22/24] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d1720b2..8962d35 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: uses: erlef/setup-beam@v1 with: otp-version: '25' - gleam-version: '0.34.1' + gleam-version: '1.1.0' rebar3-version: '3.22.1' - name: Cache dependencies From 751cb29ee6477e1fa1e90c08ae6dbc0f614d30d3 Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 29 Oct 2024 15:02:49 +0530 Subject: [PATCH 23/24] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8962d35..74f6112 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: uses: erlef/setup-beam@v1 with: otp-version: '25' - gleam-version: '1.1.0' + gleam-version: '1.5.1' rebar3-version: '3.22.1' - name: Cache dependencies From 960af719a6124dd68d39e98338e098fad9c5927f Mon Sep 17 00:00:00 2001 From: Arjun Aditya Date: Tue, 29 Oct 2024 15:06:34 +0530 Subject: [PATCH 24/24] Delete build.yml --- .github/workflows/build.yml | 42 ------------------------------------- 1 file changed, 42 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 74f6112..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Build and Test - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - build: - name: Build and test - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set up Erlang - uses: erlef/setup-beam@v1 - with: - otp-version: '25' - gleam-version: '1.5.1' - rebar3-version: '3.22.1' - - - name: Cache dependencies - uses: actions/cache@v3 - with: - path: | - _build - deps - key: ${{ runner.os }}-mix-${{ hashFiles('**/gleam.toml') }} - restore-keys: | - ${{ runner.os }}-mix- - - - name: Install dependencies - run: gleam deps download - - - name: Run build - run: gleam run build - - - name: Run tests - run: gleam test - \ No newline at end of file