Skip to content

Commit

Permalink
Rating: check vote existence, query spans, json response
Browse files Browse the repository at this point in the history
  • Loading branch information
vsilent committed Sep 24, 2023
1 parent d6b3b81 commit d8c32cb
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 32 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ tracing-subscriber = { version = "0.3.17", features = ["registry", "env-filter"]
uuid = { version = "1.3.4", features = ["v4"] }
thiserror = "1.0"
serde_valid = "0.16.3"
serde_json = "1.0.105"
serde_json = { version = "1.0.105", features = [] }
serde_derive = "1.0.188"

[dependencies.sqlx]
Expand Down
25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@ Stacker - is an application that helps users to create custom IT solutions based
source apps and user's custom applications docker containers. Users can build their own stack of applications, and
deploy the final result to their favorite clouds using TryDirect API.

Application will consist of:
1. Web UI (Application Stack builder)
2. Back-end RESTful API, includes:
a. Security module. User Authorization
b. Application Management
c. Cloud Provider Key Management
d. docker-compose generator
e. TryDirect API Client
f. Rating module

Application development will include:
- Web UI (Application Stack builder)
- Command line interface
- Back-end RESTful API, includes:
- [ ] Security module.
- [ ] User Authorization
- [ ] Application Management
- [ ] Application Key Management
- [ ] Cloud Provider Key Management
- [ ] docker-compose.yml generator
- [ ] TryDirect API Client
- [ ] Rating module

## How to start

#### Run db migration

Expand All @@ -31,6 +35,7 @@ sqlx migrate revert
```


## CURL examples
#### Rate Product

```
Expand Down
84 changes: 71 additions & 13 deletions src/routes/rating.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,41 @@
use crate::forms;
use crate::models;
use crate::startup::AppState;
use actix_web::{web, HttpResponse};
use actix_web::{web, HttpResponse, Responder, Result};
use serde_derive::Serialize;
use sqlx::PgPool;
use tracing::instrument;
use tracing::Instrument;
use uuid::Uuid;
use crate::models::RateCategory;

// 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,
id: Option<i32>
}

pub async fn rating(
app_state: web::Data<AppState>,
form: web::Json<forms::Rating>,
pool: web::Data<PgPool>,
) -> HttpResponse {
) -> 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,
r"SELECT * FROM product WHERE obj_id = $1",
form.obj_id
)
.fetch_one(pool.get_ref())
.instrument(query_span)
.await
{
Ok(product) => {
Expand All @@ -37,21 +48,55 @@ pub async fn rating(
form.obj_id,
e
);
return HttpResponse::InternalServerError().finish();
// return HttpResponse::InternalServerError().finish();
return Ok(web::Json(JsonResponse {
status : "Error".to_string(),
code: 404,
message: format!("Object not found {}", form.obj_id),
id: None
}));
}
};

let user_id = app_state.user_id; // uuid Let's assume we have a user id already taken from auth
let query_span = tracing::info_span!("Saving new rating details in the database");
let user_id = app_state.user_id; // uuid Let's assume user_id already taken from auth

let query_span = tracing::info_span!("Search for existing vote.");
match sqlx::query!(
r"SELECT id FROM rating where user_id=$1 AND product_id=$2 AND category=$3 LIMIT 1",
user_id,
form.obj_id,
form.category as RateCategory
)
.fetch_one(pool.get_ref())
.instrument(query_span)
.await
{
Ok(record) => {
tracing::info!("req_id: {} rating exists: {:?}, user: {}, product: {}, category: {:?}",
request_id, record.id, user_id, form.obj_id, form.category);

return Ok(web::Json(JsonResponse{
status: "Error".to_string(),
code: 409,
message: format!("Already Rated"),
id: Some(record.id)
}));
}
Err(err) => {
// @todo, match the sqlx response
}
}

let query_span = tracing::info_span!("Saving new rating details into the database");
// Get product by id
// Insert rating
//let category = Into::<String>::into(form.category.clone());
match sqlx::query!(
r#"
INSERT INTO rating (user_id, product_id, category, comment, hidden,rate,
created_at,
updated_at)
VALUES ($1, $2, $3, $4, $5, $6, NOW() at time zone 'utc', NOW() at time zone 'utc')
RETURNING id
"#,
user_id,
form.obj_id,
Expand All @@ -60,21 +105,34 @@ pub async fn rating(
false,
form.rate
)
.execute(pool.get_ref())
.fetch_one(pool.get_ref())
.instrument(query_span)
.await
{
Ok(_) => {
Ok(result) => {
println!("Query returned {:?}", result);
//TODO return json containing the id of the new rating
tracing::info!(
"req_id: {} New subscriber details have been saved to database",
request_id
"req_id: {} New rating {} have been saved to database",
request_id,
result.id
);
HttpResponse::Ok().finish()

Ok(web::Json(JsonResponse {
status : "ok".to_string(),
code: 200,
message: "Saved".to_string(),
id: Some(result.id)
}))
}
Err(e) => {
tracing::error!("req_id: {} Failed to execute query: {:?}", request_id, e);
HttpResponse::InternalServerError().finish()
Ok(web::Json(JsonResponse{
status: "error".to_string(),
code: 500,
message: "Failed to insert".to_string(),
id: None
}))
}
}
}
13 changes: 8 additions & 5 deletions src/routes/stack/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use sqlx::PgPool;
use tracing::Instrument;
use uuid::Uuid;
use chrono::Utc;
use crate::models::stack::FormData;
use crate::models::stack::{FormData};
use crate::startup::AppState;
use std::str;
use actix_web::web::Form;


// pub async fn add(req: HttpRequest, app_state: Data<AppState>, pool:
Expand All @@ -22,9 +23,11 @@ pub async fn add(body: Bytes) -> Result<impl Responder> {
// method 1
// let app_state: AppState = serde_json::from_str(body_str).unwrap();
// method 2
let app_state = serde_json::from_str::<AppState>(body_str).unwrap();
println!("request: {:?}", app_state);
// // println!("app: {:?}", body);
// let app_state = serde_json::from_str::<AppState>(body_str).unwrap();
// println!("request: {:?}", app_state);

let stack = serde_json::from_str::<FormData>(body_str).unwrap();
println!("app: {:?}", stack);
// println!("user_id: {:?}", data.user_id);
// tracing::info!("we are here");
// match Json::<FormData>::extract(&req).await {
Expand Down Expand Up @@ -84,6 +87,6 @@ pub async fn add(body: Bytes) -> Result<impl Responder> {
// // }

// HttpResponse::Ok().finish()
Ok(Json(app_state))
Ok(Json(stack))
// Ok(HttpResponse::Ok().finish())
}
5 changes: 3 additions & 2 deletions src/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use actix_web::{
};
use sqlx::PgPool;
use std::net::TcpListener;
use serde_derive::{Deserialize, Serialize};
use uuid::Uuid;
use serde::{Deserialize, Serialize};
// use serde_derive::{Deserialize, Serialize};
// use uuid::Uuid;

#[derive(Serialize, Deserialize, Debug)]
pub struct AppState {
Expand Down

0 comments on commit d8c32cb

Please sign in to comment.