| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- use hyper::service::{make_service_fn, service_fn};
- use hyper::{Body, Method, Request, Response, StatusCode, Server};
- pub use mysql_async::prelude::*;
- pub use mysql_async::*;
- use std::convert::Infallible;
- use std::net::SocketAddr;
- use std::result::Result;
- use std::collections::HashMap;
- use serde::{Deserialize, Serialize};
- fn get_url() -> String {
- if let Ok(url) = std::env::var("DATABASE_URL") {
- let opts = Opts::from_url(&url).expect("DATABASE_URL invalid");
- if opts
- .db_name()
- .expect("a database name is required")
- .is_empty()
- {
- panic!("database name is empty");
- }
- url
- } else {
- "mysql://root:pass@127.0.0.1:3306/mysql".into()
- }
- }
- #[derive(Serialize, Deserialize, Debug)]
- struct Order {
- order_id: i32,
- product_id: i32,
- quantity: i32,
- amount: f32,
- shipping: f32,
- tax: f32,
- shipping_address: String,
- }
- impl Order {
- fn new(
- order_id: i32,
- product_id: i32,
- quantity: i32,
- amount: f32,
- shipping: f32,
- tax: f32,
- shipping_address: String,
- ) -> Self {
- Self {
- order_id,
- product_id,
- quantity,
- amount,
- shipping,
- tax,
- shipping_address,
- }
- }
- }
- async fn handle_request(req: Request<Body>, pool: Pool) -> Result<Response<Body>, anyhow::Error> {
- match (req.method(), req.uri().path()) {
- (&Method::GET, "/") => Ok(Response::new(Body::from(
- "The valid endpoints are /init /create_order /create_orders /update_order /orders /delete_order",
- ))),
- // Simply echo the body back to the client.
- (&Method::POST, "/echo") => Ok(Response::new(req.into_body())),
- // CORS OPTIONS
- (&Method::OPTIONS, "/init") => Ok(response_build(&String::from(""))),
- (&Method::OPTIONS, "/create_order") => Ok(response_build(&String::from(""))),
- (&Method::OPTIONS, "/create_orders") => Ok(response_build(&String::from(""))),
- (&Method::OPTIONS, "/update_order") => Ok(response_build(&String::from(""))),
- (&Method::OPTIONS, "/delete_order") => Ok(response_build(&String::from(""))),
- (&Method::OPTIONS, "/orders") => Ok(response_build(&String::from(""))),
-
- (&Method::GET, "/init") => {
- let mut conn = pool.get_conn().await.unwrap();
- "DROP TABLE IF EXISTS orders;".ignore(&mut conn).await?;
- "CREATE TABLE orders (order_id INT, product_id INT, quantity INT, amount FLOAT, shipping FLOAT, tax FLOAT, shipping_address VARCHAR(20));".ignore(&mut conn).await?;
- drop(conn);
- Ok(response_build("{\"status\":true}"))
- }
- (&Method::POST, "/create_order") => {
- let mut conn = pool.get_conn().await.unwrap();
- let byte_stream = hyper::body::to_bytes(req).await?;
- let order: Order = serde_json::from_slice(&byte_stream).unwrap();
- "INSERT INTO orders (order_id, product_id, quantity, amount, shipping, tax, shipping_address) VALUES (:order_id, :product_id, :quantity, :amount, :shipping, :tax, :shipping_address)"
- .with(params! {
- "order_id" => order.order_id,
- "product_id" => order.product_id,
- "quantity" => order.quantity,
- "amount" => order.amount,
- "shipping" => order.shipping,
- "tax" => order.tax,
- "shipping_address" => &order.shipping_address,
- })
- .ignore(&mut conn)
- .await?;
- drop(conn);
- Ok(response_build("{\"status\":true}"))
- }
- (&Method::POST, "/create_orders") => {
- let mut conn = pool.get_conn().await.unwrap();
- let byte_stream = hyper::body::to_bytes(req).await?;
- let orders: Vec<Order> = serde_json::from_slice(&byte_stream).unwrap();
- "INSERT INTO orders (order_id, product_id, quantity, amount, shipping, tax, shipping_address) VALUES (:order_id, :product_id, :quantity, :amount, :shipping, :tax, :shipping_address)"
- .with(orders.iter().map(|order| {
- params! {
- "order_id" => order.order_id,
- "product_id" => order.product_id,
- "quantity" => order.quantity,
- "amount" => order.amount,
- "shipping" => order.shipping,
- "tax" => order.tax,
- "shipping_address" => &order.shipping_address,
- }
- }))
- .batch(&mut conn)
- .await?;
- drop(conn);
- Ok(response_build("{\"status\":true}"))
- }
- (&Method::POST, "/update_order") => {
- let mut conn = pool.get_conn().await.unwrap();
- let byte_stream = hyper::body::to_bytes(req).await?;
- let order: Order = serde_json::from_slice(&byte_stream).unwrap();
- "UPDATE orders SET product_id=:product_id, quantity=:quantity, amount=:amount, shipping=:shipping, tax=:tax, shipping_address=:shipping_address WHERE order_id=:order_id"
- .with(params! {
- "product_id" => order.product_id,
- "quantity" => order.quantity,
- "amount" => order.amount,
- "shipping" => order.shipping,
- "tax" => order.tax,
- "shipping_address" => &order.shipping_address,
- "order_id" => order.order_id,
- })
- .ignore(&mut conn)
- .await?;
- drop(conn);
- Ok(response_build("{\"status\":true}"))
- }
- (&Method::GET, "/orders") => {
- let mut conn = pool.get_conn().await.unwrap();
- let orders = "SELECT * FROM orders"
- .with(())
- .map(&mut conn, |(order_id, product_id, quantity, amount, shipping, tax, shipping_address)| {
- Order::new(
- order_id,
- product_id,
- quantity,
- amount,
- shipping,
- tax,
- shipping_address,
- )},
- ).await?;
- drop(conn);
- Ok(response_build(serde_json::to_string(&orders)?.as_str()))
- }
-
- (&Method::GET, "/delete_order") => {
- let mut conn = pool.get_conn().await.unwrap();
- let params: HashMap<String, String> = req.uri().query().map(|v| {
- url::form_urlencoded::parse(v.as_bytes()).into_owned().collect()
- }).unwrap_or_else(HashMap::new);
- let order_id = params.get("id");
- "DELETE FROM orders WHERE order_id=:order_id"
- .with(params! { "order_id" => order_id, })
- .ignore(&mut conn)
- .await?;
- drop(conn);
- Ok(response_build("{\"status\":true}"))
- }
- // Return the 404 Not Found for other routes.
- _ => {
- let mut not_found = Response::default();
- *not_found.status_mut() = StatusCode::NOT_FOUND;
- Ok(not_found)
- }
- }
- }
- // CORS headers
- fn response_build(body: &str) -> Response<Body> {
- Response::builder()
- .header("Access-Control-Allow-Origin", "*")
- .header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
- .header("Access-Control-Allow-Headers", "api,Keep-Alive,User-Agent,Content-Type")
- .body(Body::from(body.to_owned()))
- .unwrap()
- }
- #[tokio::main(flavor = "current_thread")]
- async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
- let opts = Opts::from_url(&*get_url()).unwrap();
- let builder = OptsBuilder::from_opts(opts);
- // The connection pool will have a min of 5 and max of 10 connections.
- let constraints = PoolConstraints::new(5, 10).unwrap();
- let pool_opts = PoolOpts::default().with_constraints(constraints);
- let pool = Pool::new(builder.pool_opts(pool_opts));
- let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
- let make_svc = make_service_fn(|_| {
- let pool = pool.clone();
- async move {
- Ok::<_, Infallible>(service_fn(move |req| {
- let pool = pool.clone();
- handle_request(req, pool)
- }))
- }
- });
- let server = Server::bind(&addr).serve(make_svc);
- if let Err(e) = server.await {
- eprintln!("server error: {}", e);
- }
- Ok(())
- }
|