From 8878b1d92bdd8ee9b51ecde7e9cc88f62f264841 Mon Sep 17 00:00:00 2001 From: Mateus Cruz Date: Mon, 5 Feb 2024 11:06:46 -0300 Subject: move routes handler module to its own file --- lib/handler.ml | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/query.ml | 76 ++++++++++++++++++++++++++++++++++++++-------------------- lib/router.ml | 49 ++----------------------------------- 3 files changed, 115 insertions(+), 73 deletions(-) create mode 100644 lib/handler.ml (limited to 'lib') 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 ] ;; -- cgit v1.2.3