Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 3 #11

Merged
merged 16 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading