From a9d654532267b1f86aa7bc9deda9dbf59e0bbe42 Mon Sep 17 00:00:00 2001 From: Franklin Date: Tue, 25 Apr 2023 08:47:48 -0400 Subject: [PATCH] Added get agent with id route --- sql/project/fetch_with_filters_paged.sql | 3 +- sqlx-data.json | 154 ++++++++++++----------- src/routes/admin.rs | 18 +-- src/routes/main_router.rs | 11 +- src/routes/read.rs | 18 ++- src/services/admin.rs | 40 ++++-- src/services/read.rs | 25 +++- src/utils/s3.rs | 22 ++-- 8 files changed, 178 insertions(+), 113 deletions(-) diff --git a/sql/project/fetch_with_filters_paged.sql b/sql/project/fetch_with_filters_paged.sql index c553474..5ec4569 100644 --- a/sql/project/fetch_with_filters_paged.sql +++ b/sql/project/fetch_with_filters_paged.sql @@ -7,7 +7,8 @@ SELECT (SELECT (SELECT u.price_usd FROM unit u WHERE u.project_id = p.id ORDER BY u.price_usd DESC LIMIT 1)) as starts_from, l.district, p.finish_date, - p.media as "media: _" + p.media as "media: _", + p.admin_tag FROM project p, location l WHERE p.location_id = l.id -- Filters here: diff --git a/sqlx-data.json b/sqlx-data.json index 6d27d63..03ae859 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -12,80 +12,6 @@ }, "query": "DELETE FROM agent WHERE id = $1;" }, - "11b388533d1cece58959754172768a0fa4bfd430ad29a3bf0eca81bbbe7ecc22": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Uuid" - }, - { - "name": "project_state: _", - "ordinal": 1, - "type_info": "Varchar" - }, - { - "name": "project_type: _", - "ordinal": 2, - "type_info": "Varchar" - }, - { - "name": "project_condition: _", - "ordinal": 3, - "type_info": "Varchar" - }, - { - "name": "city", - "ordinal": 4, - "type_info": "Varchar" - }, - { - "name": "starts_from", - "ordinal": 5, - "type_info": "Float8" - }, - { - "name": "district", - "ordinal": 6, - "type_info": "Varchar" - }, - { - "name": "finish_date", - "ordinal": 7, - "type_info": "Timestamp" - }, - { - "name": "media: _", - "ordinal": 8, - "type_info": "Text" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - null, - false, - false, - false - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Text", - "Text", - "Int2", - "Int8" - ] - } - }, - "query": "SELECT \n p.id,\n p.project_state as \"project_state: _\",\n p.project_type as \"project_type: _\",\n p.project_condition as \"project_condition: _\",\n l.city,\n (SELECT (SELECT u.price_usd FROM unit u WHERE u.project_id = p.id ORDER BY u.price_usd DESC LIMIT 1)) as starts_from,\n l.district,\n p.finish_date,\n p.media as \"media: _\"\nFROM project p, location l\nWHERE p.location_id = l.id\n-- Filters here:\nAND (LOWER(l.city) LIKE '%' || LOWER($1) || '%' OR $1 IS null) -- City Filter\nAND (LOWER(l.district) LIKE '%' || LOWER($2) || '%' OR $2 IS null) -- District Filter\nAND (p.project_type = $3 OR $3 IS null) -- ProjectType\nAND (p.project_condition = $4 OR $4 IS null) -- ProjectCondition\nAND (p.project_state = $5 OR $5 IS null) -- ProjectState\nAND ((SELECT COUNT(*) FROM unit u WHERE u.project_id = p.id AND u.rooms = $6) > 0 OR $6 IS NULL)\n-- End of filters\nORDER BY p.time_created DESC\nLIMIT 50 OFFSET $7;" - }, "19a20556f5e3621438cd583aae4984c8cf510f359f65ec599f8a6b46d9153ec4": { "describe": { "columns": [ @@ -620,6 +546,86 @@ }, "query": "INSERT INTO agent (\n id, shortcode, full_name, credential, credential_type, profile_picture_url, time_created, last_updated\n) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $7\n) RETURNING\nid,\nshortcode,\nfull_name,\ncredential,\ncredential_type as \"credential_type: _\",\nprofile_picture_url,\ntime_created,\nlast_updated;" }, + "919dc414e70785bed4b999412b2ce5c46cb07e4aefb6a8c74bde23fcca9a4759": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Uuid" + }, + { + "name": "project_state: _", + "ordinal": 1, + "type_info": "Varchar" + }, + { + "name": "project_type: _", + "ordinal": 2, + "type_info": "Varchar" + }, + { + "name": "project_condition: _", + "ordinal": 3, + "type_info": "Varchar" + }, + { + "name": "city", + "ordinal": 4, + "type_info": "Varchar" + }, + { + "name": "starts_from", + "ordinal": 5, + "type_info": "Float8" + }, + { + "name": "district", + "ordinal": 6, + "type_info": "Varchar" + }, + { + "name": "finish_date", + "ordinal": 7, + "type_info": "Timestamp" + }, + { + "name": "media: _", + "ordinal": 8, + "type_info": "Text" + }, + { + "name": "admin_tag", + "ordinal": 9, + "type_info": "Varchar" + } + ], + "nullable": [ + false, + false, + false, + false, + false, + null, + false, + false, + false, + true + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Text", + "Text", + "Int2", + "Int8" + ] + } + }, + "query": "SELECT \n p.id,\n p.project_state as \"project_state: _\",\n p.project_type as \"project_type: _\",\n p.project_condition as \"project_condition: _\",\n l.city,\n (SELECT (SELECT u.price_usd FROM unit u WHERE u.project_id = p.id ORDER BY u.price_usd DESC LIMIT 1)) as starts_from,\n l.district,\n p.finish_date,\n p.media as \"media: _\",\n p.admin_tag\nFROM project p, location l\nWHERE p.location_id = l.id\n-- Filters here:\nAND (LOWER(l.city) LIKE '%' || LOWER($1) || '%' OR $1 IS null) -- City Filter\nAND (LOWER(l.district) LIKE '%' || LOWER($2) || '%' OR $2 IS null) -- District Filter\nAND (p.project_type = $3 OR $3 IS null) -- ProjectType\nAND (p.project_condition = $4 OR $4 IS null) -- ProjectCondition\nAND (p.project_state = $5 OR $5 IS null) -- ProjectState\nAND ((SELECT COUNT(*) FROM unit u WHERE u.project_id = p.id AND u.rooms = $6) > 0 OR $6 IS NULL)\n-- End of filters\nORDER BY p.time_created DESC\nLIMIT 50 OFFSET $7;" + }, "924abcf88270de74ab6524fbd4247b2c23ab96b4569c8b81ff6b0e1d7dacada7": { "describe": { "columns": [ diff --git a/src/routes/admin.rs b/src/routes/admin.rs index 8010768..72baf52 100644 --- a/src/routes/admin.rs +++ b/src/routes/admin.rs @@ -12,19 +12,19 @@ use jl_types::{ unit::Unit, }, dto::{ + item::Item, payloads::{ agent::{NewAgentPayload, UpdateAgentPayload}, location::NewLocationPayload, project::{NewProjectPayload, UpdateProjectPayload}, unit::{NewUnitPayload, UpdateUnitPayload}, }, - item::Item, - } + }, }; use sqlx::PgPool; use uuid::Uuid; -use crate::{services}; +use crate::services; #[post("/agent")] pub async fn create_new_agent_profile( @@ -129,7 +129,11 @@ pub async fn get_all_contacts(db_conn: web::Data>) -> TypedHttpRespo services::admin::get_all_contacts(&db_conn).await } -#[post("images/{item}/{id}")] -pub async fn upload_image(aws_client: web::Data>, path_vars: Path<(Item, Uuid)>, multipart: Multipart) -> TypedHttpResponse { - services::admin::upload_image(&aws_client, path_vars.0.clone(), path_vars.1, multipart).await -} \ No newline at end of file +#[post("images/{item}")] +pub async fn upload_image( + aws_client: web::Data>, + path_vars: Path, + multipart: Multipart, +) -> TypedHttpResponse { + services::admin::upload_image(&aws_client, path_vars.clone(), multipart).await +} diff --git a/src/routes/main_router.rs b/src/routes/main_router.rs index 3c9b24e..8a749a2 100644 --- a/src/routes/main_router.rs +++ b/src/routes/main_router.rs @@ -1,7 +1,10 @@ use std::sync::Arc; use actix_cors::Cors; -use actix_web::{web::{self}, App, HttpServer}; +use actix_web::{ + web::{self}, + App, HttpServer, +}; use chrono::Utc; use reqwest::Client; use sqlx::PgPool; @@ -38,7 +41,7 @@ pub async fn start_all_routes(start_time: i64, db_conn: Arc) -> Result<( .service(super::admin::get_all_contacts) .service(super::admin::get_contacts_count) .service(super::admin::get_visits_count) - .service(super::admin::upload_image) + .service(super::admin::upload_image), ) .service( web::scope("/read") @@ -48,7 +51,9 @@ pub async fn start_all_routes(start_time: i64, db_conn: Arc) -> Result<( .service(super::read::get_projects_paged) .service(super::read::get_project_data) .service(super::read::create_contact_request) - .service(super::read::get_agent_with_shortcode), + .service(super::read::get_agent_with_shortcode) + .service(super::read::get_location_with_city_and_district) + .service(super::read::get_agent_with_id), ) }) .bind((HOST_ADDR, HOST_PORT))? diff --git a/src/routes/read.rs b/src/routes/read.rs index 9bff50f..a8bdd44 100644 --- a/src/routes/read.rs +++ b/src/routes/read.rs @@ -12,7 +12,10 @@ use actix_web::{ use actix_web_utils::extensions::typed_response::TypedHttpResponse; use err::MessageResource; use jl_types::{ - domain::{agent::Agent, project_condition::ProjectCondition, project_type::ProjectType}, + domain::{ + agent::Agent, location::Location, project_condition::ProjectCondition, + project_type::ProjectType, + }, dto::{ filters::Filter, listing::Listing, payloads::contact::ContactPayload, project_card::ProjectCardDto, @@ -28,6 +31,11 @@ pub async fn get_all_agents(db_conn: web::Data>) -> TypedHttpRespons services::read::get_all_agents(&db_conn).await } +#[get("/agent/{id}")] +pub async fn get_agent_with_id(db_conn: web::Data>, id: Path) -> TypedHttpResponse { + services::read::get_agent_with_id(&db_conn, &id).await +} + #[get("/agent/{shortcode}")] pub async fn get_agent_with_shortcode( db_conn: web::Data>, @@ -50,7 +58,13 @@ pub async fn get_locations_in_city( ) -> TypedHttpResponse> { services::read::get_all_locations_in_city(&db_conn, &city).await } - +#[get("/location/{city}/{district}")] +pub async fn get_location_with_city_and_district( + db_conn: web::Data>, + path_vars: Path<(String, String)>, +) -> TypedHttpResponse { + services::read::get_location_with_city_and_district(&db_conn, &path_vars.0, &path_vars.1).await +} #[get("/projects/{page}")] pub async fn get_projects_paged( request: HttpRequest, diff --git a/src/services/admin.rs b/src/services/admin.rs index 6bca177..852c9bc 100644 --- a/src/services/admin.rs +++ b/src/services/admin.rs @@ -1,28 +1,30 @@ use actix_multipart::Multipart; use actix_web_utils::extensions::typed_response::TypedHttpResponse; use err::MessageResource; +use futures::StreamExt as _; use jl_types::{ domain::{ agent::Agent, contact::Contact, count::Count, location::Location, project::Project, unit::Unit, }, dto::{ + item::Item, payloads::{ agent::{NewAgentPayload, UpdateAgentPayload}, location::NewLocationPayload, project::{NewProjectPayload, UpdateProjectPayload}, unit::{NewUnitPayload, UpdateUnitPayload}, }, - item::Item, - } + }, }; -use futures::StreamExt as _; - use sqlx::PgPool; use uuid::Uuid; -use crate::{dao, handle_db_read_op, handle_db_write_op, handle_tx, success, unwrap_or_not_found, utils::s3::{self}}; +use crate::{ + dao, handle_db_read_op, handle_db_write_op, handle_tx, success, unwrap_or_not_found, + utils::s3::{self}, +}; // // Insert Methods @@ -203,31 +205,45 @@ pub async fn get_contact_count(conn: &PgPool) -> TypedHttpResponse { success!(count) } -pub async fn upload_image(client: &aws_sdk_s3::Client, item: Item, id: Uuid, mut multipart: Multipart) -> TypedHttpResponse { +pub async fn upload_image( + client: &aws_sdk_s3::Client, + item: Item, + mut multipart: Multipart, +) -> TypedHttpResponse { let mut bytes: Vec = Vec::new(); match multipart.next().await { Some(item) => { let mut field = match item { Ok(field) => field, - Err(error) => return TypedHttpResponse::return_standard_error(400, MessageResource::new_from_string(error.to_string())), + Err(error) => { + return TypedHttpResponse::return_standard_error( + 400, + MessageResource::new_from_string(error.to_string()), + ) + } }; // Field in turn is stream of *Bytes* object while let Some(chunk) = field.next().await { bytes.append(&mut chunk.unwrap().to_vec()); } - }, + } None => { return TypedHttpResponse::return_empty_response(400); } - } + } if bytes.is_empty() { return TypedHttpResponse::return_empty_response(400); } - match s3::upload_image(client, id, item, Uuid::new_v4().to_string(), bytes).await { + match s3::upload_image(client, item, Uuid::new_v4().to_string(), bytes).await { Ok(url) => success!(url), - Err(error) => return TypedHttpResponse::return_standard_error(400, MessageResource::new_from_string(error.to_string())), + Err(error) => { + return TypedHttpResponse::return_standard_error( + 400, + MessageResource::new_from_string(error.to_string()), + ) + } }; -} \ No newline at end of file +} diff --git a/src/services/read.rs b/src/services/read.rs index 404a116..8a1f91f 100644 --- a/src/services/read.rs +++ b/src/services/read.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use actix_web_utils::extensions::typed_response::TypedHttpResponse; use jl_types::{ - domain::agent::Agent, + domain::{agent::Agent, location::Location}, dto::{ filters::Filter, listing::Listing, payloads::contact::ContactPayload, project_card::ProjectCardDto, @@ -27,6 +27,16 @@ pub async fn get_agent_with_shortcode( )) } +pub async fn get_agent_with_id( + conn: &PgPool, + id: &Uuid, +) -> TypedHttpResponse { + success!(unwrap_or_not_found!( + handle_db_read_op!(dao::agent::get_agent_with_id(conn, id)), + "agents" + )) +} + pub async fn get_all_locations(conn: &PgPool) -> TypedHttpResponse> { let locations: HashSet = handle_db_read_op!(dao::location::fetch_all_locations(conn)) .into_iter() @@ -47,7 +57,18 @@ pub async fn get_all_locations_in_city( .collect() ) } - +pub async fn get_location_with_city_and_district( + conn: &PgPool, + city: &String, + district: &String, +) -> TypedHttpResponse { + success!(unwrap_or_not_found!( + handle_db_read_op!(dao::location::get_locations_in_city(conn, city)) + .into_iter() + .find(|loc| &loc.district == district), + "locations" + )) +} pub async fn get_projects_paged( conn: &PgPool, page: &i64, diff --git a/src/utils/s3.rs b/src/utils/s3.rs index e22cad5..2d2e6ef 100644 --- a/src/utils/s3.rs +++ b/src/utils/s3.rs @@ -1,11 +1,7 @@ -use jl_types::dto::item::Item; use aws_sdk_s3::{ - error::SdkError, - operation::put_object::{PutObjectError}, - primitives::ByteStream, - Client, + error::SdkError, operation::put_object::PutObjectError, primitives::ByteStream, Client, }; -use uuid::Uuid; +use jl_types::dto::item::Item; pub async fn init_aws_client() -> Client { dotenv::dotenv().expect("Failed loading env"); @@ -15,12 +11,11 @@ pub async fn init_aws_client() -> Client { pub async fn upload_image( client: &Client, - id: Uuid, item: Item, file_name: String, multipart: Vec, ) -> Result> { - let path = format!("jl-images/{item}/{id}/{file_name}"); + let path = format!("jl-images/{item}/{file_name}"); match client .put_object() .bucket("jorge-ledesma-bucket") @@ -28,8 +23,11 @@ pub async fn upload_image( .body(ByteStream::from(multipart)) .acl(aws_sdk_s3::types::ObjectCannedAcl::PublicRead) .send() - .await { - Ok(_) => Ok(format!("https://jorge-ledesma-bucket.s3.us-east-2.amazonaws.com/{path}")), - Err(error) => Err(error), - } + .await + { + Ok(_) => Ok(format!( + "https://jorge-ledesma-bucket.s3.us-east-2.amazonaws.com/{path}" + )), + Err(error) => Err(error), + } }