Get Product API working fine

This commit is contained in:
Pratik Tripathy
2020-11-30 02:37:37 +05:30
commit 7692376a2b
20 changed files with 2359 additions and 0 deletions

54
src/api_error.rs Normal file
View File

@@ -0,0 +1,54 @@
use actix_web::http::StatusCode;
use actix_web::{HttpResponse, ResponseError};
use diesel::result::Error as DieselError;
use serde::Deserialize;
use serde_json::json;
use std::fmt;
#[derive(Debug, Deserialize)]
pub struct ApiError {
pub status_code: u16,
pub message: String,
}
impl ApiError {
pub fn new(status_code: u16, message: String) -> ApiError {
ApiError { status_code, message }
}
}
impl fmt::Display for ApiError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.message.as_str())
}
}
impl From<DieselError> for ApiError {
fn from(error: DieselError) -> ApiError {
match error {
DieselError::DatabaseError(_, err) => ApiError::new(409, err.message().to_string()),
DieselError::NotFound => ApiError::new(404, "Record not found".to_string()),
err => ApiError::new(500, format!("Diesel error: {}", err)),
}
}
}
impl ResponseError for ApiError {
fn error_response(&self) -> HttpResponse {
let status_code = match StatusCode::from_u16(self.status_code) {
Ok(status_code) => status_code,
Err(_) => StatusCode::INTERNAL_SERVER_ERROR,
};
let message = match status_code.as_u16() < 500 {
true => self.message.clone(),
false => {
error!("{}", self.message);
"Internal server error".to_string()
},
};
HttpResponse::build(status_code)
.json(json!({ "message": message }))
}
}

31
src/db.rs Normal file
View File

@@ -0,0 +1,31 @@
use crate::api_error::ApiError;
use diesel::pg::PgConnection;
use diesel::r2d2::ConnectionManager;
use lazy_static::lazy_static;
use r2d2;
use std::env;
type Pool = r2d2::Pool<ConnectionManager<PgConnection>>;
pub type DbConnection = r2d2::PooledConnection<ConnectionManager<PgConnection>>;
embed_migrations!();
lazy_static! {
static ref POOL: Pool = {
let db_url = env::var("DATABASE_URL").expect("Database url not set");
let manager = ConnectionManager::<PgConnection>::new(db_url);
Pool::new(manager).expect("Failed to create db pool")
};
}
pub fn init() {
info!("Initializing DB");
lazy_static::initialize(&POOL);
let conn = connection().expect("Failed to get db connection");
embedded_migrations::run(&conn).unwrap();
}
pub fn connection() -> Result<DbConnection, ApiError> {
POOL.get()
.map_err(|e| ApiError::new(500, format!("Failed getting db connection: {}", e)))
}

42
src/main.rs Normal file
View File

@@ -0,0 +1,42 @@
#[macro_use]
extern crate log;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_migrations;
use actix_web::{App, HttpServer};
use dotenv::dotenv;
use listenfd::ListenFd;
use std::env;
mod db;
mod product;
mod schema;
mod api_error;
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
dotenv().ok();
env_logger::init();
db::init();
let mut listenfd = ListenFd::from_env();
let mut server = HttpServer::new(||
App::new()
.configure(product::init_routes)
);
server = match listenfd.take_tcp_listener(0)? {
Some(listener) => server.listen(listener)?,
None => {
let host = env::var("HOST").expect("Host not set in .env file");
let port = env::var("PORT").expect("Port not set in .env file");
server.bind(format!("{}:{}", host, port))?
},
};
info!("Starting Server");
server.run().await
}

5
src/product/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
mod model;
mod routes;
pub use model::Product;
pub use routes::init_routes;

52
src/product/model.rs Normal file
View File

@@ -0,0 +1,52 @@
use crate::api_error::ApiError;
use crate::db;
use crate::schema::products;
use chrono::{NaiveDateTime, Utc};
use diesel::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, AsChangeset)]
#[table_name = "products"]
pub struct ProductMessage {
pub productid: i32,
pub productname: String,
}
#[derive(Serialize, Deserialize, Queryable, Insertable)]
#[table_name = "products"]
pub struct Product {
pub productid: i32,
pub productname: String,
pub created_at: NaiveDateTime
}
impl Product {
pub fn find_all() -> Result<Vec<Self>, ApiError> {
let conn = db::connection()?;
let products = products::table
.load::<Product>(&conn)?;
Ok(products)
}
pub fn find(id: i32) -> Result<Self, ApiError> {
let conn = db::connection()?;
let product = products::table
.filter(products::productid.eq(id))
.first(&conn)?;
Ok(product)
}
}
impl From<ProductMessage> for Product {
fn from(product: ProductMessage) -> Self {
Product {
productid: product.productid,
productname: product.productname,
created_at: Utc::now().naive_utc(),
}
}
}

20
src/product/routes.rs Normal file
View File

@@ -0,0 +1,20 @@
// src/product/routes.rs
use crate::product::Product;
use actix_web::{get, post, web, HttpResponse, Responder};
#[get("/products")]
async fn find_all() -> impl Responder {
let products = Product::find_all().expect("Error fetching all Products");
HttpResponse::Ok().json(products)
}
#[get("/product/{id}")]
async fn find(id: web::Path<i32>) -> impl Responder {
let product = Product::find(id.into_inner()).expect("Error fetching Product");
HttpResponse::Ok().json(product)
}
pub fn init_routes(cfg: &mut web::ServiceConfig) {
cfg.service(find_all);
cfg.service(find);
}

32
src/schema.rs Normal file
View File

@@ -0,0 +1,32 @@
table! {
order_details (orderid, productid) {
orderid -> Int4,
productid -> Int4,
quantity -> Int4,
}
}
table! {
orders (orderid) {
customer_email -> Text,
orderid -> Int4,
created_at -> Timestamp,
}
}
table! {
products (productid) {
productid -> Int4,
productname -> Text,
created_at -> Timestamp,
}
}
joinable!(order_details -> orders (orderid));
joinable!(order_details -> products (productid));
allow_tables_to_appear_in_same_query!(
order_details,
orders,
products,
);