Skip to content

Commit

Permalink
wip: periodic, pgo
Browse files Browse the repository at this point in the history
Signed-off-by: Guillaume Hivert <[email protected]>
  • Loading branch information
ghivert committed Apr 12, 2024
1 parent 1cc78fa commit 97b96d9
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 21 deletions.
4 changes: 4 additions & 0 deletions apps/backend/gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ radiate = {path = "../../packages/gleam-radiate"}
wisp = "~> 0.14"
simplifile = "~> 1.7"
gleam_pgo = "~> 0.7"
pgo = "~> 0.14"
dot_env = "~> 0.5"
gleam_otp = "~> 0.10"
gleam_package_interface = "~> 1.0"

[dev-dependencies]
gleeunit = "~> 1.0"
8 changes: 7 additions & 1 deletion apps/backend/manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "976CFF85D34D50F7775896615A71745FBE0C325E50399787088F941B539A0497" },
{ name = "dot_env", version = "0.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "simplifile"], otp_app = "dot_env", source = "hex", outer_checksum = "18F51CAFE99F6E3F2B10CF5DBACE63505DB6DC1FA69AFA0105756ACC8201C3A2" },
{ 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 = "filespy", version = "0.3.0", build_tools = ["gleam"], requirements = ["fs", "gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "filespy", source = "hex", outer_checksum = "75F5910B31A528681D25316AAAE6C91CD3E977BD2492946564B7242FF941FB7A" },
Expand All @@ -13,6 +14,7 @@ packages = [
{ 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 = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "8B197DD5D578EA6AC2C0D4BDC634C71A5BCA8E7DB5F47091C263ECB411A60DF3" },
{ 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_package_interface", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_package_interface", source = "hex", outer_checksum = "52A721BCA972C8099BB881195D821AAA64B9F2655BECC102165D5A1097731F01" },
{ name = "gleam_pgo", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "pgo"], otp_app = "gleam_pgo", source = "hex", outer_checksum = "0EF5061A2D7FC063AFDF8B4E796D5B3E866C1E5D674111F4C8CF3A7E24D40270" },
{ name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" },
{ name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" },
Expand All @@ -34,13 +36,17 @@ packages = [
]

[requirements]
dot_env = { version = "~> 0.5" }
gleam_erlang = { version = "~> 0.25" }
gleam_http = { version = "~> 3.6" }
gleam_json = { version = "~> 1.0" }
gleam_pgo = { version = "~> 0.7"}
gleam_otp = { version = "~> 0.10" }
gleam_package_interface = { version = "~> 1.0"}
gleam_pgo = { version = "~> 0.7" }
gleam_stdlib = { version = "~> 0.34 or ~> 1.0" }
gleeunit = { version = "~> 1.0" }
mist = { version = "1.0.0-rc2" }
pgo = { version = "~> 0.14" }
radiate = { path = "../../packages/gleam-radiate" }
simplifile = { version = "~> 1.7" }
wisp = { version = "~> 0.14" }
30 changes: 28 additions & 2 deletions apps/backend/src/backend.gleam
Original file line number Diff line number Diff line change
@@ -1,20 +1,46 @@
import backend/config.{type Config}
import backend/postgres
import backend/router
import dot_env
import gleam/erlang/process
import gleam/otp/supervisor
import mist
import periodic
import setup
import wisp

pub fn main() {
setup.radiate()
wisp.configure_logger()
dot_env.load()

let secret_key_base = setup.get_secret_key_base()
let secret_key_base = config.get_secret_key_base()
let cnf = config.read_config()

let assert Ok(_) =
wisp.mist_handler(router.handle_request, secret_key_base)
router.handle_request(_, cnf)
|> wisp.mist_handler(secret_key_base)
|> mist.new()
|> mist.port(3000)
|> mist.start_http()

process.sleep_forever()
}

fn supervise(start: fn() -> _) {
use children <- supervisor.start()
supervisor.add(children, {
use _ <- supervisor.worker()
start()
})
}

fn sync_hex(cnf: Config) {
let db = postgres.connect(cnf)
Ok(True)
}

fn start_hex_sync(cnf: Config) {
use <- supervise()
periodic.periodically(do: fn() { sync_hex(cnf) }, waiting: 60 * 1000)
}
24 changes: 24 additions & 0 deletions apps/backend/src/backend/config.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import gleam/erlang/os
import gleam/pgo
import wisp

pub type Context {
Context(connection: pgo.Connection)
}

pub type Config {
Config(database_url: String)
}

pub fn read_config() {
let assert Ok(database_url) = os.get_env("DATABASE_URL")
Config(database_url)
}

pub fn get_secret_key_base() {
wisp.random_string(64)
}

pub fn is_dev() {
os.get_env("GLEAM_ENV") == Ok("development")
}
41 changes: 41 additions & 0 deletions apps/backend/src/backend/postgres.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import backend/config.{type Config, type Context, Context}
import gleam/dynamic
import gleam/int
import gleam/list
import gleam/option.{Some}
import gleam/pgo.{Config}
import gleam/regex
import gleam/result
import wisp.{type Response}

pub fn connect(cnf: Config) {
let assert Ok(config) = parse_database_url(cnf.database_url)
config
|> pgo.connect()
|> Context
}

pub fn middleware(cnf: Config, handler: fn(Context) -> Response) {
connect(cnf)
|> handler()
}

fn database_url_matcher() {
"postgres://(.*):(.*)@(.*):(.*)/(.*)"
|> regex.from_string()
|> result.replace_error(Nil)
}

fn parse_database_url(database_url: String) -> Result(pgo.Config, _) {
use matcher <- result.map(database_url_matcher())
let matches = regex.scan(with: matcher, content: database_url)
use cnf, data, index <- list.index_fold(matches, pgo.default_config())
case index {
0 -> Config(..cnf, user: data.content)
1 -> Config(..cnf, password: Some(data.content))
2 -> Config(..cnf, host: data.content)
3 -> Config(..cnf, port: result.unwrap(int.parse(data.content), 5432))
4 -> Config(..cnf, database: data.content)
_ -> cnf
}
}
9 changes: 6 additions & 3 deletions apps/backend/src/backend/router.gleam
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import backend/config.{type Config}
import backend/postgres
import backend/web.{type Handler}
import gleam/string_builder
import wisp.{type Request, type Response}
import backend/web

fn empty_json() {
let content = "{}"
Expand All @@ -9,9 +11,10 @@ fn empty_json() {
|> wisp.json_response(200)
}

pub fn handle_request(req: Request) -> Response {
use req <- web.middleware(req)
pub fn handle_request(req: Request, cnf: Config) -> Response {
use req <- web.foundations(req)
use req <- web.reroute_non_json_request(req)
use ctx <- postgres.middleware(cnf)
case wisp.path_segments(req) {
[] -> empty_json()
_ -> wisp.not_found()
Expand Down
8 changes: 4 additions & 4 deletions apps/backend/src/backend/web.gleam
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import backend/config
import backend/request
import gleam/http
import setup
import wisp.{type Request, type Response}

type Handler =
pub type Handler =
fn(Request) -> Response

pub fn middleware(req: Request, handler: Handler) -> Response {
pub fn foundations(req: Request, handler: Handler) -> Response {
use <- wisp.log_request(req)
use <- wisp.rescue_crashes()
use req <- wisp.handle_head(req)
handler(req)
}

pub fn reroute_non_json_request(req: Request, handler: Handler) -> Response {
case req.method, request.is_json_request(req), setup.is_dev() {
case req.method, request.is_json_request(req), config.is_dev() {
http.Get, True, _ -> handler(req)
http.Get, False, True -> wisp.redirect("http://localhost:5173")
http.Get, False, False -> wisp.ok()
Expand Down
55 changes: 55 additions & 0 deletions apps/backend/src/periodic.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import gleam/erlang/process.{type Subject}
import gleam/io
import gleam/function
import gleam/otp/actor
import gleam/result

// import packages/error.{type Error}

pub opaque type Message {
Rerun
}

type State(a) {
State(self: Subject(Message), work: fn() -> Result(a, Nil), interval: Int)
}

/// Repeatedly call a function, leaving `interval` milliseconds between each
/// call.
/// When the `work` function returns an error it is printed.
pub fn periodically(
do work: fn() -> Result(a, Nil),
waiting interval: Int,
) -> Result(Subject(Message), actor.StartError) {
fn() { init(interval, work) }
|> actor.Spec(loop: loop, init_timeout: 100)
|> actor.start_spec()
}

fn init(
interval: Int,
work: fn() -> Result(a, Nil),
) -> actor.InitResult(State(a), Message) {
let subject = process.new_subject()
let state = State(subject, work, interval)
process.new_selector()
|> process.selecting(subject, function.identity)
|> fn(selector) {
enqueue_next_rerun(state)
actor.Ready(state, selector)
}
}

fn loop(message: Message, state: State(a)) -> actor.Next(Message, State(a)) {
case message {
Rerun -> {
let _ = result.map_error(state.work(), io.debug)
enqueue_next_rerun(state)
actor.continue(state)
}
}
}

fn enqueue_next_rerun(state: State(a)) {
process.send_after(state.self, state.interval, Rerun)
}
13 changes: 2 additions & 11 deletions apps/backend/src/setup.gleam
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import gleam/erlang/os
import backend/config
import gleam/io
import radiate
import wisp

pub fn is_dev() {
os.get_env("GLEAM_ENV") == Ok("development")
}

pub fn get_secret_key_base() {
wisp.random_string(64)
}

fn print_radiate_update(_state: state, path: String) {
io.println("Change in " <> path <> ", reloading")
}

pub fn radiate() {
case is_dev() {
case config.is_dev() {
False -> Nil
True -> {
let assert Ok(_) =
Expand Down
6 changes: 6 additions & 0 deletions apps/backend/src/tasks/hex.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import backend/postgres
import backend/config.{type Config}

pub fn sync(cnf: Config) {
let ctx = postgres.connect(cnf)
}

0 comments on commit 97b96d9

Please sign in to comment.