Implement read all budgets request
This commit is contained in:
parent
cd013d0009
commit
5b3323f39c
11 changed files with 185 additions and 18 deletions
|
@ -24,6 +24,16 @@ pub struct BudgetEntity {
|
||||||
pub status: StatusType,
|
pub status: StatusType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, FromRow)]
|
||||||
|
pub struct BudgetWithPermissionValueEntity {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub icon: Option<String>,
|
||||||
|
pub permission_name: String,
|
||||||
|
pub permission_value: i32,
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn insert_new_budget_for_user(
|
pub async fn insert_new_budget_for_user(
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
new_budget: NewBudgetEntity,
|
new_budget: NewBudgetEntity,
|
||||||
|
@ -48,3 +58,61 @@ pub async fn insert_new_budget_for_user(
|
||||||
|
|
||||||
Ok((new_budget, budget_permissions))
|
Ok((new_budget, budget_permissions))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_all_budgets_for_user(
|
||||||
|
pool: &DbPool,
|
||||||
|
user_id: i32,
|
||||||
|
) -> Result<Vec<BudgetWithPermissionValueEntity>, AppError> {
|
||||||
|
sqlx::query_as::<_, BudgetWithPermissionValueEntity>(
|
||||||
|
"SELECT
|
||||||
|
b.id as id,
|
||||||
|
b.name as name,
|
||||||
|
b.description as description,
|
||||||
|
b.icon as icon,
|
||||||
|
p.name as permission_name,
|
||||||
|
p.value as permission_value
|
||||||
|
FROM
|
||||||
|
public.budget b
|
||||||
|
INNER JOIN public.user_budget_permission ubp ON b.id = ubp.budget_id
|
||||||
|
INNER JOIN public.permission p on ubp.permission_id = p.id
|
||||||
|
WHERE
|
||||||
|
ubp.user_id = $1
|
||||||
|
ORDER BY
|
||||||
|
b.id,
|
||||||
|
p.value;",
|
||||||
|
)
|
||||||
|
.bind(user_id)
|
||||||
|
.fetch_all(pool)
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
error!(
|
||||||
|
?err,
|
||||||
|
user_id, "Cannot fetch permission values for the user's budgets"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_err(AppError::from)
|
||||||
|
.map(|all_budgets| {
|
||||||
|
all_budgets
|
||||||
|
.chunk_by(|a, b| a.id == b.id)
|
||||||
|
.map(|budgets| {
|
||||||
|
budgets.into_iter().fold(
|
||||||
|
BudgetWithPermissionValueEntity::default(),
|
||||||
|
|acc, entity| BudgetWithPermissionValueEntity {
|
||||||
|
id: entity.id,
|
||||||
|
name: entity.name.clone(),
|
||||||
|
description: entity.description.clone(),
|
||||||
|
icon: entity.icon.clone(),
|
||||||
|
permission_name: acc
|
||||||
|
.permission_name
|
||||||
|
.split('|')
|
||||||
|
.filter(|name| !name.is_empty())
|
||||||
|
.chain([entity.permission_name.as_str()])
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("|"),
|
||||||
|
permission_value: acc.permission_value + entity.permission_value,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub async fn associate_budget_with_user_as_owner(
|
||||||
let values = get_all_permissions_by_category(pool, PermissionCategoryType::Budget)
|
let values = get_all_permissions_by_category(pool, PermissionCategoryType::Budget)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|permission| format!("({user_id}, {budget_id}, {})", permission.id))
|
.map(|permission| format!("({user_id}, '{budget_id}', {})", permission.id))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(",");
|
.join(",");
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use http::StatusCode;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{DbPool, NewAccountEntity, insert_new_account_for_user},
|
db::{DbPool, NewAccountEntity, insert_new_account_for_user},
|
||||||
models::{AppError, Session},
|
models::{ApiResponse, AppError, Session},
|
||||||
requests::AppState,
|
requests::AppState,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,14 +22,18 @@ pub async fn account_creation_post_handler(
|
||||||
let pool = state.db_pool();
|
let pool = state.db_pool();
|
||||||
let user_id = session.user_id;
|
let user_id = session.user_id;
|
||||||
|
|
||||||
account_creation_request(pool, user_id, request).await
|
account_creation_request(pool, user_id, request)
|
||||||
|
.await
|
||||||
|
.map(|(status_code, response)| {
|
||||||
|
(status_code, ApiResponse::new(response).into_json_response()).into_response()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn account_creation_request(
|
async fn account_creation_request(
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
request: AccountCreationRequest,
|
request: AccountCreationRequest,
|
||||||
) -> Result<Response, AppError> {
|
) -> Result<(StatusCode, AccountCreationResponse), AppError> {
|
||||||
let AccountCreationRequest {
|
let AccountCreationRequest {
|
||||||
r#type: account_type,
|
r#type: account_type,
|
||||||
name,
|
name,
|
||||||
|
@ -48,5 +52,5 @@ async fn account_creation_request(
|
||||||
.await
|
.await
|
||||||
.map(|response| AccountCreationResponse::from(response))?;
|
.map(|response| AccountCreationResponse::from(response))?;
|
||||||
|
|
||||||
Ok((StatusCode::CREATED, Json(response)).into_response())
|
Ok((StatusCode::CREATED, response))
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,23 +23,19 @@ pub async fn account_read_all_get_request(
|
||||||
|
|
||||||
account_read_all_request(pool, user_id)
|
account_read_all_request(pool, user_id)
|
||||||
.await
|
.await
|
||||||
.map(|(status_code, response)| {
|
.map(|response| (StatusCode::OK, Json(ApiResponse::new(response))).into_response())
|
||||||
(status_code, Json(ApiResponse::new(response))).into_response()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn account_read_all_request(
|
async fn account_read_all_request(
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
) -> Result<(StatusCode, Vec<AccountsReadAllResponse>), AppError> {
|
) -> Result<Vec<AccountsReadAllResponse>, AppError> {
|
||||||
let accounts = get_all_accounts_for_user(pool, user_id)
|
get_all_accounts_for_user(pool, user_id)
|
||||||
.await
|
.await
|
||||||
.map(|accounts| {
|
.map(|accounts| {
|
||||||
accounts
|
accounts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(AccountsReadAllResponse::from)
|
.map(AccountsReadAllResponse::from)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})?;
|
})
|
||||||
|
|
||||||
Ok((StatusCode::OK, accounts))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use uuid::Uuid;
|
||||||
use crate::db::{AccountType, AccountWithPermissionValueEntity};
|
use crate::db::{AccountType, AccountWithPermissionValueEntity};
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct AccountsReadAllResponse {
|
pub struct AccountsReadAllResponse {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub account_type: AccountType,
|
pub account_type: AccountType,
|
||||||
|
|
|
@ -7,7 +7,7 @@ use http::StatusCode;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{DbPool, NewBudgetEntity, insert_new_budget_for_user},
|
db::{DbPool, NewBudgetEntity, insert_new_budget_for_user},
|
||||||
models::{AppError, Session},
|
models::{ApiResponse, AppError, Session},
|
||||||
requests::AppState,
|
requests::AppState,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,7 +24,9 @@ pub async fn budget_create_post_request(
|
||||||
|
|
||||||
budget_creation_request(pool, user_id, request)
|
budget_creation_request(pool, user_id, request)
|
||||||
.await
|
.await
|
||||||
.map(|(status_code, response)| (status_code, Json(response)).into_response())
|
.map(|(status_code, response)| {
|
||||||
|
(status_code, ApiResponse::new(response).into_json_response()).into_response()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn budget_creation_request(
|
async fn budget_creation_request(
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
use axum::{Router, routing::post};
|
mod create;
|
||||||
|
mod read;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Router,
|
||||||
|
routing::{get, post},
|
||||||
|
};
|
||||||
use create::budget_create_post_request;
|
use create::budget_create_post_request;
|
||||||
|
use read::budget_read_all_get_request;
|
||||||
|
|
||||||
use super::AppState;
|
use super::AppState;
|
||||||
|
|
||||||
mod create;
|
|
||||||
|
|
||||||
pub fn requests(state: AppState) -> Router {
|
pub fn requests(state: AppState) -> Router {
|
||||||
Router::new().nest(
|
Router::new().nest(
|
||||||
"/budget",
|
"/budget",
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", post(budget_create_post_request))
|
.route("/", post(budget_create_post_request))
|
||||||
|
.route("/", get(budget_read_all_get_request))
|
||||||
.with_state(state),
|
.with_state(state),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
47
api/src/requests/budget/read/handler.rs
Normal file
47
api/src/requests/budget/read/handler.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use axum::{
|
||||||
|
debug_handler,
|
||||||
|
extract::State,
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
use http::StatusCode;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
db::{DbPool, get_all_budgets_for_user},
|
||||||
|
models::{ApiResponse, AppError, Session},
|
||||||
|
requests::AppState,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::models::BudgetReadAllResponse;
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn budget_read_all_get_request(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
session: Session,
|
||||||
|
) -> Result<Response, AppError> {
|
||||||
|
let pool = state.db_pool();
|
||||||
|
let user_id = session.user_id;
|
||||||
|
|
||||||
|
budget_read_all_request(pool, user_id)
|
||||||
|
.await
|
||||||
|
.map(|response| {
|
||||||
|
(
|
||||||
|
StatusCode::OK,
|
||||||
|
ApiResponse::new(response).into_json_response(),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn budget_read_all_request(
|
||||||
|
pool: &DbPool,
|
||||||
|
user_id: i32,
|
||||||
|
) -> Result<Vec<BudgetReadAllResponse>, AppError> {
|
||||||
|
get_all_budgets_for_user(pool, user_id)
|
||||||
|
.await
|
||||||
|
.map(|budgets| {
|
||||||
|
budgets
|
||||||
|
.into_iter()
|
||||||
|
.map(BudgetReadAllResponse::from)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
}
|
4
api/src/requests/budget/read/mod.rs
Normal file
4
api/src/requests/budget/read/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
mod handler;
|
||||||
|
pub mod models;
|
||||||
|
|
||||||
|
pub use handler::*;
|
3
api/src/requests/budget/read/models/mod.rs
Normal file
3
api/src/requests/budget/read/models/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
mod response;
|
||||||
|
|
||||||
|
pub use response::*;
|
36
api/src/requests/budget/read/models/response.rs
Normal file
36
api/src/requests/budget/read/models/response.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::db::BudgetWithPermissionValueEntity;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct BudgetReadAllResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub icon: Option<String>,
|
||||||
|
pub permission_name: String,
|
||||||
|
pub permission_value: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BudgetWithPermissionValueEntity> for BudgetReadAllResponse {
|
||||||
|
fn from(other: BudgetWithPermissionValueEntity) -> Self {
|
||||||
|
let BudgetWithPermissionValueEntity {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
icon,
|
||||||
|
permission_name,
|
||||||
|
permission_value,
|
||||||
|
} = other;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
icon,
|
||||||
|
permission_name,
|
||||||
|
permission_value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue