diff --git a/databases/mysql/Cargo.toml b/databases/mysql/Cargo.toml new file mode 100644 index 00000000..cca64226 --- /dev/null +++ b/databases/mysql/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "mysql" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +actix-web = "4.3.1" +serde = { version = "1.0.136", features = ["derive"] } +mysql = "24.0.0" +dotenv = "0.15.0" +chrono = "0.4.26" diff --git a/databases/mysql/README.md b/databases/mysql/README.md new file mode 100644 index 00000000..95dc1770 --- /dev/null +++ b/databases/mysql/README.md @@ -0,0 +1,80 @@ +# mysql + +This RESTful Actix Web API examples illustrates how to connect to MySQL database using Mysql client library implemented in rust i.e MySql database driver + +Below APIs are supported: +- Add Bank +- Add Branch +- Add Teller +- Add Customer +- Get Bank +- Get Branch +- Get Teller +- Get Customer + +The RESTful Actix Web API has below listed dependencies: +- [Actix Web](https://github.com/actix/actix-web) web framework for Rust +- [Serde](https://github.com/serde-rs/serde) for serializing and deserializing Rust data structures +- [MySQL](https://github.com/mysql/mysql-server) MySQL database server +- [mysql](https://github.com/blackbeam/rust-mysql-simple) MySql database driver + +## Instructions + +### NOTE: + +You may need to ensure that you are running the commands with the correct MySQL user/password. + +1. Access MySQL Server + + Log in to the MySQL Server using a user account that has the CREATE DATABASE privilege. + +2. Create database + + ```sql + CREATE DATABASE my_bank; + ``` + +3. Create tables in the database + + Directory "mysql\sql" contains below listed ".sql" files: + - bankdetails.sql + - branch_details.sql + - teller_details.sql + - customer_details.sql + + Copy the contents of each of the ".sql" and execute them separately on MySQL Server. This will create four tables in the database. + +4. Create `.env` file: + + ```ini + SERVER_ADDR=127.0.0.1:8080 + MYSQL_USER=XXX + MYSQL_PASSWORD=XXX + MYSQL_HOST=127.0.0.1 + MYSQL_PORT=3306 + MYSQL_DBNAME=my_bank + ``` + + Update "MYSQL_USER" and "MYSQL_PASSWORD" values with the correct MySQL user/password. + If your password contains dollar sign "$", then remember to escape it eg "123$abc" will need to be changed to "123\\$abc" + +5. Run the server: + + ```shell + cargo run + ``` + +6. Using a different terminal send an HTTP GET/POST requests to the running server: + + Directory "mysql\apis" contains below listed api's files: + - addbank.txt + - addbranch.txt + - addteller.txt + - addcustomer.txt + - getbank.txt + - getbranch.txt + - getteller.txt + - getcustomer.txt + + Copy the curl request on each of the ".txt" and execute them on separate terminals. Each ".txt" contains curl request and expected json reponse data. + \ No newline at end of file diff --git a/databases/mysql/apis/addbank.txt b/databases/mysql/apis/addbank.txt new file mode 100644 index 00000000..738cbb61 --- /dev/null +++ b/databases/mysql/apis/addbank.txt @@ -0,0 +1,17 @@ +1. +Linux + +curl -d '{"bank_name":"Bank ABC","country":"Kenya"}' -H 'Content-Type: application/json' http://127.0.0.1:8080/addbank + +2. +Windows + +curl -H "Content-type:application/json" --data-binary "{\"bank_name\":\"Bank ABC\",\"country\":\"Kenya\"}" http://127.0.0.1:8080/addbank + +3. +Json response + +{ + "status_code": 0, + "status_description": "Successful" +} \ No newline at end of file diff --git a/databases/mysql/apis/addbranch.txt b/databases/mysql/apis/addbranch.txt new file mode 100644 index 00000000..5ef3a7ba --- /dev/null +++ b/databases/mysql/apis/addbranch.txt @@ -0,0 +1,18 @@ +1. +Linux + +curl -d '{"branch_name":"HQ branch", "location":"Central Business District"}' -H 'Content-Type: application/json' http://127.0.0.1:8080/addbranch + +2. +Windows + +curl -H "Content-type:application/json" --data-binary "{\"branch_name\":\"HQ branch\", \"location\":\"Central Business District\"}" http://127.0.0.1:8080/addbranch + + +3. +Json response + +{ + "status_code": 0, + "status_description": "Successful" +} \ No newline at end of file diff --git a/databases/mysql/apis/addcustomer.txt b/databases/mysql/apis/addcustomer.txt new file mode 100644 index 00000000..839259d1 --- /dev/null +++ b/databases/mysql/apis/addcustomer.txt @@ -0,0 +1,18 @@ +1. +Linux + +curl -d '{"customer_name":"Peter Paul", "branch_name":"Central Business District"}' -H 'Content-Type: application/json' http://127.0.0.1:8080/addcustomer + +2. +Windows + +curl -H "Content-type:application/json" --data-binary "{\"customer_name\":\"Peter Paul\", \"branch_name\":\"Central Business District\"}" http://127.0.0.1:8080/addcustomer + + +3. +Json response + +{ + "status_code": 0, + "status_description": "Successful" +} \ No newline at end of file diff --git a/databases/mysql/apis/addteller.txt b/databases/mysql/apis/addteller.txt new file mode 100644 index 00000000..77f05a4f --- /dev/null +++ b/databases/mysql/apis/addteller.txt @@ -0,0 +1,18 @@ +1. +Linux + +curl -d '{"teller_name":"John Doe", "branch_name":"Central Business District"}' -H 'Content-Type: application/json' http://127.0.0.1:8080/addteller + +2. +Windows + +curl -H "Content-type:application/json" --data-binary "{\"teller_name\":\"John Doe\", \"branch_name\":\"Central Business District\"}" http://127.0.0.1:8080/addteller + + +3. +Json response + +{ + "status_code": 0, + "status_description": "Successful" +} \ No newline at end of file diff --git a/databases/mysql/apis/getbank.txt b/databases/mysql/apis/getbank.txt new file mode 100644 index 00000000..0edf28dc --- /dev/null +++ b/databases/mysql/apis/getbank.txt @@ -0,0 +1,23 @@ +1. +Linux + +curl 'http://localhost:8080/getbank' + +2. +Windows + +curl http://localhost:8080/getbank + +3. +Json response + +{ + "status_code": 0, + "status_description": "Successful", + "bank_data": [ + { + "bank_name": "bank abc", + "country": "kenya" + } + ] +} \ No newline at end of file diff --git a/databases/mysql/apis/getbranch.txt b/databases/mysql/apis/getbranch.txt new file mode 100644 index 00000000..40d15a96 --- /dev/null +++ b/databases/mysql/apis/getbranch.txt @@ -0,0 +1,23 @@ +1. +Linux + +curl 'http://localhost:8080/getbranch' + +2. +Windows + +curl http://localhost:8080/getbranch + +3. +Json response + +{ + "status_code": 0, + "status_description": "Successful", + "branch_data": [ + { + "branch_name": "hq branch", + "location": "central business district" + } + ] +} \ No newline at end of file diff --git a/databases/mysql/apis/getcustomer.txt b/databases/mysql/apis/getcustomer.txt new file mode 100644 index 00000000..ef027e4e --- /dev/null +++ b/databases/mysql/apis/getcustomer.txt @@ -0,0 +1,23 @@ +1. +Linux + +curl 'http://localhost:8080/getcustomer' + +2. +Windows + +curl http://localhost:8080/getcustomer + +3. +Json response + +{ + "status_code": 0, + "status_description": "Successful", + "customer_data": [ + { + "customer_name": "peter paul", + "branch_name": "central business district" + } + ] +} \ No newline at end of file diff --git a/databases/mysql/apis/getteller.txt b/databases/mysql/apis/getteller.txt new file mode 100644 index 00000000..1b81f403 --- /dev/null +++ b/databases/mysql/apis/getteller.txt @@ -0,0 +1,23 @@ +1. +Linux + +curl 'http://localhost:8080/getteller' + +2. +Windows + +curl http://localhost:8080/getteller + +3. +Json response + +{ + "status_code": 0, + "status_description": "Successful", + "teller_data": [ + { + "teller_name": "john doe", + "branch_name": "central business district" + } + ] +} \ No newline at end of file diff --git a/databases/mysql/sql/bankdetails.sql b/databases/mysql/sql/bankdetails.sql new file mode 100644 index 00000000..7969a3cd --- /dev/null +++ b/databases/mysql/sql/bankdetails.sql @@ -0,0 +1,7 @@ +CREATE TABLE `bank_details` ( + `id` int NOT NULL AUTO_INCREMENT, + `bank_name` varchar(30) DEFAULT '', + `country` varchar(30) DEFAULT '', + `date_added` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; diff --git a/databases/mysql/sql/branch_details.sql b/databases/mysql/sql/branch_details.sql new file mode 100644 index 00000000..e8da81f9 --- /dev/null +++ b/databases/mysql/sql/branch_details.sql @@ -0,0 +1,7 @@ +CREATE TABLE `branch_details` ( + `id` int NOT NULL AUTO_INCREMENT, + `branch_name` varchar(30) DEFAULT '', + `location` varchar(30) DEFAULT '', + `date_added` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; diff --git a/databases/mysql/sql/customer_details.sql b/databases/mysql/sql/customer_details.sql new file mode 100644 index 00000000..d4daf204 --- /dev/null +++ b/databases/mysql/sql/customer_details.sql @@ -0,0 +1,7 @@ +CREATE TABLE `customer_details` ( + `id` int NOT NULL AUTO_INCREMENT, + `customer_name` varchar(100) DEFAULT '', + `branch_name` varchar(30) DEFAULT '', + `date_added` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; diff --git a/databases/mysql/sql/teller_details.sql b/databases/mysql/sql/teller_details.sql new file mode 100644 index 00000000..1234d769 --- /dev/null +++ b/databases/mysql/sql/teller_details.sql @@ -0,0 +1,7 @@ +CREATE TABLE `teller_details` ( + `id` int NOT NULL AUTO_INCREMENT, + `teller_name` varchar(100) DEFAULT '', + `branch_name` varchar(30) DEFAULT '', + `date_added` datetime DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; diff --git a/databases/mysql/src/db_layer.rs b/databases/mysql/src/db_layer.rs new file mode 100644 index 00000000..ffa7874a --- /dev/null +++ b/databases/mysql/src/db_layer.rs @@ -0,0 +1,418 @@ +use crate::BankDetails; +use crate::BankResponseData; +use crate::BranchDetails; +use crate::BranchResponseData; +use crate::CustomerDetails; +use crate::CustomerResponseData; +use crate::ResponseStatus; +use crate::TellerDetails; +use crate::TellerResponseData; +use actix_web::web; +use mysql::prelude::*; +use mysql::*; + +const ERROR_MESSAGE: &str = "Error occured during processing, please try again."; + +pub fn create_bank(data: &web::Data, bank_name: String, _country: String) -> ResponseStatus { + let my_status_code: u8 = 1; + let my_status_description: String = ERROR_MESSAGE.to_string(); + + let mut response_status = ResponseStatus { + status_code: my_status_code, + status_description: my_status_description, + }; + + if bank_name.replace(" ", "").trim().len() == 0 { + response_status.status_description = String::from("Bank name is empty!"); + return response_status; + } + + if _country.replace(" ", "").trim().len() == 0 { + response_status.status_description = String::from("Country is empty!"); + return response_status; + } + + match data.get_conn().and_then(|mut conn| { + insert_bank_data(&mut conn, bank_name.to_lowercase(), _country.to_lowercase()) + }) { + Ok(x) => { + if x > 0 { + response_status.status_code = 0; + response_status.status_description = String::from("Successful"); + } + } + Err(e) => println!("Failed to open DB connection. create_bank {:?}", e), + } + + response_status +} + +pub fn create_branch( + data: &web::Data, + branch_name: String, + _location: String, +) -> ResponseStatus { + let my_status_code: u8 = 1; + let my_status_description: String = ERROR_MESSAGE.to_string(); + + let mut response_status = ResponseStatus { + status_code: my_status_code, + status_description: my_status_description, + }; + + if branch_name.replace(" ", "").trim().len() == 0 { + response_status.status_description = String::from("Branch name is empty!"); + return response_status; + } + + if _location.replace(" ", "").trim().len() == 0 { + response_status.status_description = String::from("Location is empty!"); + return response_status; + } + + match data.get_conn().and_then(|mut conn| { + insert_branch_data( + &mut conn, + branch_name.to_lowercase(), + _location.to_lowercase(), + ) + }) { + Ok(x) => { + if x > 0 { + response_status.status_code = 0; + response_status.status_description = String::from("Successful"); + } + } + Err(e) => println!("Failed to open DB connection. create_branch {:?}", e), + } + + response_status +} + +pub fn create_teller( + data: &web::Data, + teller_name: String, + branch_name: String, +) -> ResponseStatus { + let my_status_code: u8 = 1; + let my_status_description: String = ERROR_MESSAGE.to_string(); + + let mut response_status = ResponseStatus { + status_code: my_status_code, + status_description: my_status_description, + }; + + if teller_name.replace(" ", "").trim().len() == 0 { + response_status.status_description = String::from("Teller name is empty!"); + return response_status; + } + + if branch_name.replace(" ", "").trim().len() == 0 { + response_status.status_description = String::from("Branch name is empty!"); + return response_status; + } + + match data.get_conn().and_then(|mut conn| { + insert_teller_data( + &mut conn, + teller_name.to_lowercase(), + branch_name.to_lowercase(), + ) + }) { + Ok(x) => { + if x > 0 { + response_status.status_code = 0; + response_status.status_description = String::from("Successful"); + } + } + Err(e) => println!("Failed to open DB connection. create_teller {:?}", e), + } + + response_status +} + +pub fn create_customer( + data: &web::Data, + customer_name: String, + branch_name: String, +) -> ResponseStatus { + let my_status_code: u8 = 1; + let my_status_description: String = ERROR_MESSAGE.to_string(); + + let mut response_status = ResponseStatus { + status_code: my_status_code, + status_description: my_status_description, + }; + + if customer_name.replace(" ", "").trim().len() == 0 { + response_status.status_description = String::from("Customer name is empty!"); + return response_status; + } + + if branch_name.replace(" ", "").trim().len() == 0 { + response_status.status_description = String::from("Branch name is empty!"); + return response_status; + } + + match data.get_conn().and_then(|mut conn| { + insert_customer_data( + &mut conn, + customer_name.to_lowercase(), + branch_name.to_lowercase(), + ) + }) { + Ok(x) => { + if x > 0 { + response_status.status_code = 0; + response_status.status_description = String::from("Successful"); + } + } + Err(e) => println!("Failed to open DB connection. create_customer {:?}", e), + } + + response_status +} + +pub fn get_bank_data(data: &web::Data) -> BankResponseData { + let mut vec_bank_data = Vec::new(); + let mut my_status_code: u8 = 1; + let mut my_status_description: String = String::from("Record not found"); + + match data + .get_conn() + .and_then(|mut conn| select_bank_details(&mut conn)) + { + Ok(s) => { + vec_bank_data = s; + } + Err(e) => println!("Failed to open DB connection. {:?}", e), + } + + if vec_bank_data.len() > 0 { + my_status_code = 0; + my_status_description = String::from("Successful"); + } + + //Assign values to struct variable + let output_data = BankResponseData { + status_code: my_status_code, + status_description: my_status_description, + bank_data: vec_bank_data, + }; + + output_data +} + +pub fn get_branch_data(data: &web::Data) -> BranchResponseData { + let mut vec_branch_data = Vec::new(); + let mut my_status_code: u8 = 1; + let mut my_status_description: String = String::from("Record not found"); + + match data + .get_conn() + .and_then(|mut conn| select_branch_details(&mut conn)) + { + Ok(s) => { + vec_branch_data = s; + } + Err(e) => println!("Failed to open DB connection. {:?}", e), + } + + if vec_branch_data.len() > 0 { + my_status_code = 0; + my_status_description = String::from("Successful"); + } + + //Assign values to struct variable + let output_data = BranchResponseData { + status_code: my_status_code, + status_description: my_status_description, + branch_data: vec_branch_data, + }; + + output_data +} + +pub fn get_teller_data(data: &web::Data) -> TellerResponseData { + let mut vec_teller_data = Vec::new(); + let mut my_status_code: u8 = 1; + let mut my_status_description: String = String::from("Record not found"); + + match data + .get_conn() + .and_then(|mut conn| select_teller_details(&mut conn)) + { + Ok(s) => { + vec_teller_data = s; + } + Err(e) => println!("Failed to open DB connection. {:?}", e), + } + + if vec_teller_data.len() > 0 { + my_status_code = 0; + my_status_description = String::from("Successful"); + } + + //Assign values to struct variable + let output_data = TellerResponseData { + status_code: my_status_code, + status_description: my_status_description, + teller_data: vec_teller_data, + }; + + output_data +} + +pub fn get_customer_data(data: &web::Data) -> CustomerResponseData { + let mut vec_customer_data = Vec::new(); + let mut my_status_code: u8 = 1; + let mut my_status_description: String = String::from("Record not found"); + + match data + .get_conn() + .and_then(|mut conn| select_customer_details(&mut conn)) + { + Ok(s) => { + vec_customer_data = s; + } + Err(e) => println!("Failed to open DB connection. {:?}", e), + } + + if vec_customer_data.len() > 0 { + my_status_code = 0; + my_status_description = String::from("Successful"); + } + + //Assign values to struct variable + let output_data = CustomerResponseData { + status_code: my_status_code, + status_description: my_status_description, + customer_data: vec_customer_data, + }; + + output_data +} + +fn insert_bank_data( + conn: &mut PooledConn, + my_bank_name: String, + my_country: String, +) -> std::result::Result { + // Insert data into the database table bank_details + conn.exec_drop( + "insert into bank_details (bank_name, country) values (:bank_name, :country);", + params! { + "bank_name" => my_bank_name, + "country" => my_country, + }, + ) + .and_then(|_| Ok(conn.last_insert_id())) +} + +fn insert_branch_data( + conn: &mut PooledConn, + my_branch_name: String, + my_location: String, +) -> std::result::Result { + // Insert data into the database table branch_details + conn.exec_drop( + "insert into branch_details (branch_name, location) values (:branch_name, :location);", + params! { + "branch_name" => my_branch_name, + "location" => my_location, + }, + ) + .and_then(|_| Ok(conn.last_insert_id())) +} + +fn insert_teller_data( + conn: &mut PooledConn, + my_teller_name: String, + my_branch_name: String, +) -> std::result::Result { + // Insert data into the database table teller_details + conn.exec_drop( + "insert into teller_details (teller_name, branch_name) values (:teller_name, :branch_name);", + params! { + "teller_name" => my_teller_name, + "branch_name" => my_branch_name, + }, + ) + .and_then(|_| Ok(conn.last_insert_id())) +} + +fn insert_customer_data( + conn: &mut PooledConn, + my_customer_name: String, + my_branch_name: String, +) -> std::result::Result { + // Insert data into the database table customer_details + conn.exec_drop( + "insert into customer_details (customer_name, branch_name) values (:customer_name, :branch_name);", + params! { + "customer_name" => my_customer_name, + "branch_name" => my_branch_name, + }, + ) + .and_then(|_| Ok(conn.last_insert_id())) +} + +fn select_bank_details( + conn: &mut PooledConn, +) -> std::result::Result, mysql::error::Error> { + let mut bank_data = Vec::new(); + + conn.query_map( + "select bank_name, country from bank_details where length(trim(coalesce(bank_name,''))) > 0 and length(trim(coalesce(country,''))) > 0 order by id asc;", + |(my_bank_name, my_country)| { + let bank_details = BankDetails { bank_name: my_bank_name, country: my_country, }; + bank_data.push(bank_details); + }, + ) + .and_then(|_| Ok(bank_data)) +} + +fn select_branch_details( + conn: &mut PooledConn, +) -> std::result::Result, mysql::error::Error> { + let mut branch_data = Vec::new(); + + conn.query_map( + "select branch_name, location from branch_details where length(trim(coalesce(branch_name,''))) > 0 and length(trim(coalesce(location,''))) > 0 order by id asc;", + |(my_branch_name, my_location)| { + let branch_details = BranchDetails { branch_name: my_branch_name, location: my_location, }; + branch_data.push(branch_details); + }, + ) + .and_then(|_| Ok(branch_data)) +} + +fn select_teller_details( + conn: &mut PooledConn, +) -> std::result::Result, mysql::error::Error> { + let mut teller_data = Vec::new(); + + conn.query_map( + "select teller_name, branch_name from teller_details where length(trim(coalesce(teller_name,''))) > 0 and length(trim(coalesce(branch_name,''))) > 0 order by id asc;", + |(my_teller_name, my_branch_name)| { + let teller_details = TellerDetails { teller_name: my_teller_name, branch_name: my_branch_name, }; + teller_data.push(teller_details); + }, + ) + .and_then(|_| Ok(teller_data)) +} + +fn select_customer_details( + conn: &mut PooledConn, +) -> std::result::Result, mysql::error::Error> { + let mut customer_data = Vec::new(); + + conn.query_map( + "select customer_name, branch_name from customer_details where length(trim(coalesce(customer_name,''))) > 0 and length(trim(coalesce(branch_name,''))) > 0 order by id asc;", + |(my_customer_name, my_branch_name)| { + let teller_details = CustomerDetails { customer_name: my_customer_name, branch_name: my_branch_name, }; + customer_data.push(teller_details); + }, + ) + .and_then(|_| Ok(customer_data)) +} diff --git a/databases/mysql/src/main.rs b/databases/mysql/src/main.rs new file mode 100644 index 00000000..7ec59cd8 --- /dev/null +++ b/databases/mysql/src/main.rs @@ -0,0 +1,246 @@ +mod db_layer; + +use actix_web::{get, post, web, App, HttpServer, Responder}; +use dotenv::dotenv; +use mysql::*; +use serde::{Deserialize, Serialize}; +use std::env; +use std::str; + +#[derive(Deserialize)] +struct BankData { + bank_name: String, + country: String, +} + +#[derive(Deserialize)] +struct BranchData { + branch_name: String, + location: String, +} + +#[derive(Deserialize)] +struct TellerData { + teller_name: String, + branch_name: String, +} + +#[derive(Deserialize)] +struct CustomerData { + customer_name: String, + branch_name: String, +} + +// output +#[derive(Serialize)] +pub struct ResponseStatus { + pub status_code: u8, + pub status_description: String, +} + +#[derive(Serialize)] +pub struct BankDetails { + pub bank_name: String, + pub country: String, +} + +#[derive(Serialize)] +pub struct BankResponseData { + pub status_code: u8, + pub status_description: String, + pub bank_data: Vec, +} + +#[derive(Serialize)] +pub struct BranchDetails { + pub branch_name: String, + pub location: String, +} + +#[derive(Serialize)] +pub struct BranchResponseData { + pub status_code: u8, + pub status_description: String, + pub branch_data: Vec, +} + +#[derive(Serialize)] +pub struct TellerDetails { + pub teller_name: String, + pub branch_name: String, +} + +#[derive(Serialize)] +pub struct TellerResponseData { + pub status_code: u8, + pub status_description: String, + pub teller_data: Vec, +} + +#[derive(Serialize)] +pub struct CustomerDetails { + pub customer_name: String, + pub branch_name: String, +} + +#[derive(Serialize)] +pub struct CustomerResponseData { + pub status_code: u8, + pub status_description: String, + pub customer_data: Vec, +} + +#[get("/")] +async fn index() -> impl Responder { + format!("") +} + +#[post("/addbank")] +async fn add_bank(bank_data: web::Json, data: web::Data) -> impl Responder { + let bank_name = &bank_data.bank_name; + let _country = &bank_data.country; + + let response_data = db_layer::create_bank(&data, bank_name.to_string(), _country.to_string()); + + web::Json(response_data) +} + +#[post("/addbranch")] +async fn add_branch(branch_data: web::Json, data: web::Data) -> impl Responder { + let branch_name = &branch_data.branch_name; + let _location = &branch_data.location; + + let response_data = + db_layer::create_branch(&data, branch_name.to_string(), _location.to_string()); + + web::Json(response_data) +} + +#[post("/addteller")] +async fn add_teller(teller_data: web::Json, data: web::Data) -> impl Responder { + let teller_name = &teller_data.teller_name; + let branch_name = &teller_data.branch_name; + + let response_data = + db_layer::create_teller(&data, teller_name.to_string(), branch_name.to_string()); + + web::Json(response_data) +} + +#[post("/addcustomer")] +async fn add_customer( + customer_data: web::Json, + data: web::Data, +) -> impl Responder { + let customer_name = &customer_data.customer_name; + let branch_name = &customer_data.branch_name; + + let response_data = + db_layer::create_customer(&data, customer_name.to_string(), branch_name.to_string()); + + web::Json(response_data) +} + +#[get("/getbank")] +async fn get_bank(data: web::Data) -> impl Responder { + let bank_response_data = db_layer::get_bank_data(&data); + + web::Json(bank_response_data) +} + +#[get("/getbranch")] +async fn get_branch(data: web::Data) -> impl Responder { + let branch_response_data = db_layer::get_branch_data(&data); + + web::Json(branch_response_data) +} + +#[get("/getteller")] +async fn get_teller(data: web::Data) -> impl Responder { + let teller_response_data = db_layer::get_teller_data(&data); + + web::Json(teller_response_data) +} + +#[get("/getcustomer")] +async fn get_customer(data: web::Data) -> impl Responder { + let customer_response_data = db_layer::get_customer_data(&data); + + web::Json(customer_response_data) +} + +fn get_conn_builder( + db_user: String, + db_password: String, + db_host: String, + db_port: u16, + db_name: String, +) -> OptsBuilder { + let builder = OptsBuilder::new() + .ip_or_hostname(Some(db_host)) + .tcp_port(db_port) + .db_name(Some(db_name)) + .user(Some(db_user)) + .pass(Some(db_password)); + builder +} + +#[actix_web::main] +async fn main() { + // get env vars + dotenv().ok(); + let server_addr = env::var("SERVER_ADDR").expect("SERVER_ADDR is not set in .env file"); + let db_user = env::var("MYSQL_USER").expect("MYSQL_USER is not set in .env file"); + let db_password = env::var("MYSQL_PASSWORD").expect("MYSQL_PASSWORD is not set in .env file"); + let db_host = env::var("MYSQL_HOST").expect("MYSQL_HOST is not set in .env file"); + let my_db_port = env::var("MYSQL_PORT").expect("MYSQL_PORT is not set in .env file"); + let db_name = env::var("MYSQL_DBNAME").expect("MYSQL_DBNAME is not set in .env file"); + let mut http_server_status = String::from("[info] ActixWebHttpServer - Listening for HTTP on "); + let db_port: u16 = match my_db_port.parse::() { + Ok(a) => a, + Err(e) => 0, + }; + + http_server_status.push_str(&server_addr); + + let builder: OptsBuilder = get_conn_builder(db_user, db_password, db_host, db_port, db_name); + let pool = match Pool::new(builder) { + Ok(pool) => pool, + Err(e) => { + println!("Failed to open DB connection. {:?}", e); + return; + } + }; + + let shared_data = web::Data::new(pool); + + let server = match HttpServer::new(move || { + App::new() + .app_data(shared_data.clone()) + .service(index) + .service(add_bank) + .service(add_branch) + .service(add_teller) + .service(add_customer) + .service(get_bank) + .service(get_branch) + .service(get_teller) + .service(get_customer) + }) + .bind(server_addr) + { + Ok(s) => { + println!("{:?}", http_server_status); + s + } + Err(e) => { + println!("Failed to bind port. {:?}", e); + return; + } + }; + + match server.run().await { + Ok(_) => println!("Server exited normally."), + Err(e) => println!("Server exited with error: {:?}", e), + }; +}