diff options
author | Mateus Cruz <mateuscolvr@gmail.com> | 2024-02-05 11:06:46 -0300 |
---|---|---|
committer | Mateus Cruz <mateuscolvr@gmail.com> | 2024-02-05 11:06:46 -0300 |
commit | 8878b1d92bdd8ee9b51ecde7e9cc88f62f264841 (patch) | |
tree | 46d6c47dfc820689baa552d97255adeda4c98b15 | |
parent | 5a2f5bf9a12b270e15757df48afeb1c1db5b34b3 (diff) |
move routes handler module to its own file
-rw-r--r-- | bin/main.ml | 8 | ||||
-rw-r--r-- | docker-compose.yml | 4 | ||||
-rw-r--r-- | lib/handler.ml | 63 | ||||
-rw-r--r-- | lib/query.ml | 76 | ||||
-rw-r--r-- | lib/router.ml | 49 | ||||
-rw-r--r-- | script.sql | 4 |
6 files changed, 120 insertions, 84 deletions
diff --git a/bin/main.ml b/bin/main.ml index 99cc0e0..3f11200 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1,12 +1,9 @@ -[@@@warning "-26-27-32"] - -open! StdLabels open Eio open Piaf let request_handler ~db_pool Server.{ request; _ } = match Rinha.Router.match_route request.meth request.target with - | Some handler -> handler db_pool request + | Some handler -> Result.get_ok @@ handler db_pool request | None -> Response.create `Not_found ;; @@ -25,7 +22,8 @@ let () = Uri.make ~scheme:"postgres" ~userinfo:"admin:123" ~host:"db" ~path:"rinha" () in let db_pool = - Result.get_ok @@ Caqti_eio.connect_pool ~sw ~stdenv:(env :> Caqti_eio.stdenv) db_uri + Result.get_ok + @@ Caqti_eio_unix.connect_pool ~sw ~stdenv:(env :> Caqti_eio.stdenv) db_uri in let server = Server.create ~config (request_handler ~db_pool) in ignore @@ Server.Command.start ~sw env server diff --git a/docker-compose.yml b/docker-compose.yml index 03ed942..9c58e3f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,8 +5,6 @@ services: image: rinha:latest environment: - DB_HOST=db - ports: - - 8081:8080 depends_on: - db deploy: @@ -19,8 +17,6 @@ services: <<: *api environment: - DB_HOST=db - ports: - - 8082:8080 nginx: image: nginx:latest diff --git a/lib/handler.ml b/lib/handler.ml new file mode 100644 index 0000000..4ee7189 --- /dev/null +++ b/lib/handler.ml @@ -0,0 +1,63 @@ +open Piaf + +let create_transaction client_id (db_pool : Query.pool) (request : Request.t) = + Caqti_eio.Pool.use + (fun conn -> + let client_opt = + Option.join @@ Result.to_option @@ Query.find_client client_id conn + in + match client_opt with + | Some _client -> + let insert_result = + let body = Result.to_option @@ Body.to_string request.body in + let json = Option.map Yojson.Safe.from_string body in + let decoded_op = Option.bind json (Utils.Decoder.decode Operation.decoder) in + match decoded_op with + | Some op -> + (match Query.execute_operation ~client_id ~op conn with + | Ok _ as ok -> ok + | Error e -> Error (`DB e)) + | None -> Error (`Decoder "Invalid operation") + in + (match insert_result with + | Ok () -> + let json : Yojson.Safe.t = + `Assoc [ "limite", `Int 100000; "saldo", `Int (-9098) ] + in + Ok (Response.of_string ~body:(Yojson.Safe.to_string json) `OK) + | Error _ -> Ok (Response.create (`Code 422))) + | None -> Ok (Response.create `Not_found)) + db_pool +;; + +let get_balance client_id (db_pool : Query.pool) (_request : Request.t) = + Caqti_eio.Pool.use + (fun conn -> + let client_opt = + Option.join @@ Result.to_option @@ Query.find_client client_id conn + in + match client_opt with + | Some _client -> + let client_balance_opt = + Option.join @@ Result.to_option @@ Query.balance client_id conn + in + (match client_balance_opt with + | Some (balance_value, time) -> + let json : Yojson.Safe.t = + let balance = + let total = `Int balance_value in + let date = + `String + (Format.asprintf "%a" (Ptime.pp_rfc3339 ~tz_offset_s:(-10800) ()) time) + in + let limit = `Int 100000 in + `Assoc [ "total", total; "data_extrato", date; "limite", limit ] + in + let last_transactions = `List [] in + `Assoc [ "saldo", balance; "ultimas_transacoes", last_transactions ] + in + Ok (Response.of_string ~body:(Yojson.Safe.to_string json) `OK) + | None -> Ok (Response.create `Not_found)) + | None -> Ok (Response.create `Not_found)) + db_pool +;; diff --git a/lib/query.ml b/lib/query.ml index 0fb6f7e..85f91f0 100644 --- a/lib/query.ml +++ b/lib/query.ml @@ -1,37 +1,61 @@ type pool = ((module Rapper_helper.CONNECTION), Caqti_error.t) Caqti_eio.Pool.t -let transaction_query = - [%rapper - execute - {sql| +module Q = struct + let transaction = + [%rapper + execute + {sql| INSERT INTO transactions (client_id, value, type, description) VALUES (%int{client_id}, %int{value}, %Operation.TransactionType{transaction_type}, %string{description}) |sql}] -;; + ;; -let client_query = - let open Client in - [%rapper - get_opt - {sql| - SELECT @int{id}, @int{mov_limit} FROM clients WHERE id = %int{id} - |sql} - record_out] -;; + let debit = + [%rapper + execute + {sql| + UPDATE balances SET value = value - %int{value} WHERE client_id = %int{client_id} + |sql}] + ;; + + let credit = + [%rapper + execute + {sql| + UPDATE balances SET value = value + %int{value} WHERE client_id = %int{client_id} + |sql}] + ;; + + let client = + let open Client in + [%rapper + get_opt + {sql| + SELECT @int{id}, @int{mov_limit} FROM clients WHERE id = %int{id} + |sql} + record_out] + ;; + + let balance = + [%rapper + get_opt + {sql| + SELECT @int{value}, @ptime{now()} as time FROM balances WHERE client_id = %int{client_id} + |sql}] + ;; +end + +let ( let* ) = Result.bind -let execute_operation ~client_id ~(op : Operation.t) pool = +let execute_operation ~client_id ~(op : Operation.t) conn = match op with - | Transaction data -> - Caqti_eio.Pool.use - (fun conn -> - transaction_query - ~client_id - ~value:data.value - ~transaction_type:data.transaction_type - ~description:data.description - conn) - pool + | Transaction { value; description; transaction_type } -> + let* () = Q.transaction ~client_id ~value ~description conn ~transaction_type in + (match transaction_type with + | Credit -> Q.credit ~value ~client_id conn + | Debit -> Q.debit ~value ~client_id conn) | _ -> failwith "TODO" ;; -let find_client id pool = Caqti_eio.Pool.use (fun conn -> client_query ~id conn) pool +let find_client id conn = Q.client ~id conn +let balance client_id conn = Q.balance ~client_id conn diff --git a/lib/router.ml b/lib/router.ml index 35cef34..4572978 100644 --- a/lib/router.ml +++ b/lib/router.ml @@ -1,5 +1,3 @@ -[@@@warning "-26-27-32"] - open StdLabels open Routes open Piaf @@ -14,55 +12,12 @@ module R = Map.Make (struct ;; end) -module Handler = struct - let transaction client_id (db_pool : Query.pool) (request : Request.t) = - let client_opt = - Option.join @@ Result.to_option @@ Query.find_client client_id db_pool - in - match client_opt with - | Some client -> - let insert_result = - let body = Result.to_option @@ Body.to_string request.body in - let json = Option.map Yojson.Safe.from_string body in - let decoded_op = Option.bind json (Utils.Decoder.decode Operation.decoder) in - match decoded_op with - | Some op -> - (match Query.execute_operation ~client_id ~op db_pool with - | Ok _ as ok -> ok - | Error e -> Error (`DB e)) - | None -> Error (`Decoder "Invalid operation") - in - (match insert_result with - | Ok () -> - let json : Yojson.Safe.t = - `Assoc [ "limite", `Int 100000; "saldo", `Int (-9098) ] - in - Response.of_string ~body:(Yojson.Safe.to_string json) `OK - | Error _ -> Response.create (`Code 422)) - | None -> Response.create `Not_found - ;; - - let balance client_id (db_pool : Query.pool) (request : Request.t) = - let json : Yojson.Safe.t = - let balance = - let total = `Int (-9098) in - let date = `String "2024-01-17T02:34:41.217753Z" in - let limit = `Int 100000 in - `Assoc [ "total", total; "data_extrato", date; "limite", limit ] - in - let last_transactions = `List [] in - `Assoc [ "saldo", balance; "ultimas_transacoes", last_transactions ] - in - Response.of_string ~body:(Yojson.Safe.to_string json) `OK - ;; -end - let routes = List.fold_left ~f:(fun acc (v, r) -> R.add_to_list v r acc) ~init:R.empty - [ `GET, (s "clientes" / int / s "extrato" /? nil) @--> Handler.balance - ; `POST, (s "clientes" / int / s "transacoes" /? nil) @--> Handler.transaction + [ `GET, (s "clientes" / int / s "extrato" /? nil) @--> Handler.get_balance + ; `POST, (s "clientes" / int / s "transacoes" /? nil) @--> Handler.create_transaction ] ;; @@ -11,8 +11,8 @@ CREATE TABLE transactions ( client_id INTEGER REFERENCES clients, value INTEGER NOT NULL, type transaction_type NOT NULL, - description VARCHAR(10) NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT NOW() + description VARCHAR(255) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE balances ( |