Skip to content

Commit

Permalink
Merge pull request #11 from trydirect/issue-3
Browse files Browse the repository at this point in the history
Issue 3
  • Loading branch information
vsilent authored Oct 15, 2023
2 parents 0da15a6 + de55c95 commit 938853c
Show file tree
Hide file tree
Showing 14 changed files with 209 additions and 78 deletions.
33 changes: 33 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ serde_json = { version = "1.0.105", features = [] }
serde_derive = "1.0.188"
actix-web-httpauth = "0.8.1"
actix-cors = "0.6.4"
tracing-actix-web = "0.7.7"

[dependencies.sqlx]
version = "0.6.3"
Expand Down
3 changes: 2 additions & 1 deletion configuration.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
application_port: 8000
auth_url: https://65190108818c4e98ac6000e4.mockapi.io/user/1
database:
host: localhost
port: 5432
username: postgres
password: "postgres"
database_name: stacker
database_name: stacker
1 change: 1 addition & 0 deletions src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use serde;
pub struct Settings {
pub database: DatabaseSettings,
pub application_port: u16,
pub auth_url: String,
}

#[derive(Debug, serde::Deserialize)]
Expand Down
1 change: 1 addition & 0 deletions src/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod serialize_datetime;
21 changes: 21 additions & 0 deletions src/helpers/serialize_datetime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use chrono::{DateTime, TimeZone, Utc};
use serde::{Deserialize, Deserializer, Serializer};

const FORMAT: &'static str = "%Y-%m-%d %H:%M:%S";

pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = format!("{}", date.format(FORMAT));
serializer.serialize_str(&s)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Utc.datetime_from_str(&s, FORMAT)
.map_err(serde::de::Error::custom)
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod configuration;
pub mod forms;
pub mod helpers;
mod middleware;
pub mod models;
pub mod routes;
Expand Down
13 changes: 1 addition & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use sqlx::PgPool;
use stacker::configuration::get_configuration;
use stacker::startup::run;
use stacker::telemetry::{get_subscriber, init_subscriber};
Expand All @@ -9,16 +8,6 @@ async fn main() -> std::io::Result<()> {
init_subscriber(subscriber);

let configuration = get_configuration().expect("Failed to read configuration.");
let connection_pool = PgPool::connect(&configuration.database.connection_string())
.await
.expect("Failed to connect to database.");
let address = format!("127.0.0.1:{}", configuration.application_port);
tracing::info!("Start server at {:?}", &address);
let listener = std::net::TcpListener::bind(address).expect(&format!(
"failed to bind to {}",
configuration.application_port
));

run(listener, connection_pool)?.await
run(configuration).await?.await
}

12 changes: 7 additions & 5 deletions src/models/rating.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

pub struct Product {
// Product - is an external object that we want to store in the database,
Expand All @@ -17,15 +16,18 @@ pub struct Product {
pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Serialize)]
pub struct Rating {
pub id: i32,
pub user_id: Uuid, // external user_id, 100, taken using token (middleware?)
pub user_id: i32, // external user_id, 100, taken using token (middleware?)
pub product_id: i32, //primary key, for better data management
pub category: String, // rating of product | rating of service etc
pub comment: String, // always linked to a product
pub hidden: bool, // rating can be hidden for non-adequate user behaviour
pub rate: u32,
pub comment: Option<String>, // always linked to a product
pub hidden: Option<bool>, // rating can be hidden for non-adequate user behaviour
pub rate: Option<i32>,
#[serde(with = "crate::helpers::serialize_datetime")]
pub created_at: DateTime<Utc>,
#[serde(with = "crate::helpers::serialize_datetime")]
pub updated_at: DateTime<Utc>,
}

Expand Down
6 changes: 3 additions & 3 deletions src/routes/health_checks.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use actix_web::{HttpRequest, HttpResponse};
use actix_web::{get, HttpRequest, HttpResponse};

pub async fn health_check(req: HttpRequest) -> HttpResponse {
#[get("")]
pub async fn health_check(_req: HttpRequest) -> HttpResponse {
HttpResponse::Ok().finish()
}

44 changes: 19 additions & 25 deletions src/routes/rating.rs → src/routes/rating/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use crate::forms;
use crate::models;
use crate::models::user::User;
use crate::models::RateCategory;
use actix_web::{web, HttpResponse, Responder, Result};
use actix_web::post;
use actix_web::{web, Responder, Result};
use serde_derive::Serialize;
use sqlx::PgPool;
use tracing::Instrument;
use uuid::Uuid;

// workflow
// add, update, list, get(user_id), ACL,
Expand All @@ -21,13 +21,13 @@ struct JsonResponse {
id: Option<i32>,
}

pub async fn rating(
#[tracing::instrument(name = "Add rating.")]
#[post("")]
pub async fn add_handler(
user: web::ReqData<User>,
form: web::Json<forms::Rating>,
pool: web::Data<PgPool>,
) -> Result<impl Responder> {
//TODO. check if there already exists a rating for this product committed by this user
let request_id = Uuid::new_v4();
let query_span = tracing::info_span!("Check product existence by id.");
match sqlx::query_as!(
models::Product,
Expand All @@ -39,16 +39,10 @@ pub async fn rating(
.await
{
Ok(product) => {
tracing::info!("req_id: {} Found product: {:?}", request_id, product.obj_id);
tracing::info!("Found product: {:?}", product.obj_id);
}
Err(e) => {
tracing::error!(
"req_id: {} Failed to fetch product: {:?}, error: {:?}",
request_id,
form.obj_id,
e
);
// return HttpResponse::InternalServerError().finish();
tracing::error!("Failed to fetch product: {:?}, error: {:?}", form.obj_id, e);
return Ok(web::Json(JsonResponse {
status: "Error".to_string(),
code: 404,
Expand All @@ -71,8 +65,7 @@ pub async fn rating(
{
Ok(record) => {
tracing::info!(
"req_id: {} rating exists: {:?}, user: {}, product: {}, category: {:?}",
request_id,
"rating exists: {:?}, user: {}, product: {}, category: {:?}",
record.id,
user.id,
form.obj_id,
Expand All @@ -86,13 +79,19 @@ pub async fn rating(
id: Some(record.id),
}));
}
Err(err) => {
// @todo, match the sqlx response
Err(sqlx::Error::RowNotFound) => {}
Err(e) => {
tracing::error!("Failed to fetch rating, error: {:?}", e);
return Ok(web::Json(JsonResponse {
status: "Error".to_string(),
code: 500,
message: format!("Internal Server Error"),
id: None,
}));
}
}

let query_span = tracing::info_span!("Saving new rating details into the database");
// Get product by id
// Insert rating
match sqlx::query!(
r#"
Expand All @@ -114,12 +113,7 @@ pub async fn rating(
.await
{
Ok(result) => {
println!("Query returned {:?}", result);
tracing::info!(
"req_id: {} New rating {} have been saved to database",
request_id,
result.id
);
tracing::info!("New rating {} have been saved to database", result.id);

Ok(web::Json(JsonResponse {
status: "ok".to_string(),
Expand All @@ -129,7 +123,7 @@ pub async fn rating(
}))
}
Err(e) => {
tracing::error!("req_id: {} Failed to execute query: {:?}", request_id, e);
tracing::error!("Failed to execute query: {:?}", e);
Ok(web::Json(JsonResponse {
status: "error".to_string(),
code: 500,
Expand Down
65 changes: 65 additions & 0 deletions src/routes/rating/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::models;
use actix_web::get;
use actix_web::{web, Responder, Result};
use serde_derive::Serialize;
use sqlx::PgPool;
use tracing::Instrument;

// workflow
// add, update, list, get(user_id), ACL,
// ACL - access to func for a user
// ACL - access to objects for a user

#[derive(Serialize)]
struct JsonResponse {
status: String,
message: String,
code: u32,
rating: Option<models::Rating>,
}

#[tracing::instrument(name = "Get rating.")]
#[get("/{id}")]
pub async fn get_handler(
path: web::Path<(i32,)>,
pool: web::Data<PgPool>,
) -> Result<impl Responder> {
let rate_id = path.0;
let query_span = tracing::info_span!("Search for rate id={}.", rate_id);
match sqlx::query_as!(
models::Rating,
r"SELECT * FROM rating WHERE id=$1 LIMIT 1",
rate_id
)
.fetch_one(pool.get_ref())
.instrument(query_span)
.await
{
Ok(rating) => {
tracing::info!("rating found: {:?}", rating.id,);
return Ok(web::Json(JsonResponse {
status: "Success".to_string(),
code: 200,
message: "".to_string(),
rating: Some(rating),
}));
}
Err(sqlx::Error::RowNotFound) => {
return Ok(web::Json(JsonResponse {
status: "Error".to_string(),
code: 404,
message: format!("Not Found"),
rating: None,
}));
}
Err(e) => {
tracing::error!("Failed to fetch rating, error: {:?}", e);
return Ok(web::Json(JsonResponse {
status: "Error".to_string(),
code: 500,
message: format!("Internal Server Error"),
rating: None,
}));
}
}
}
4 changes: 4 additions & 0 deletions src/routes/rating/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod add;
mod get;
pub use add::*;
pub use get::*;
Loading

0 comments on commit 938853c

Please sign in to comment.