multi tenancy part 1
This commit is contained in:
@@ -1,11 +1,6 @@
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
Json,
|
||||
};
|
||||
use axum::{extract::Path, http::StatusCode, response::IntoResponse, Json};
|
||||
use chrono::Utc;
|
||||
use diesel::{dsl::count_star, prelude::*, result::DatabaseErrorKind, PgConnection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -13,10 +8,10 @@ use serde_json::Value;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
auth::TenantScopedConn,
|
||||
error::{AppError, AppResult},
|
||||
models::{Correspondent, NewCorrespondent},
|
||||
schema::{correspondents, document_correspondents},
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
use super::documents::to_iso;
|
||||
@@ -59,15 +54,19 @@ struct CorrespondentChangeset<'a> {
|
||||
}
|
||||
|
||||
pub async fn list_correspondents(
|
||||
State(state): State<AppState>,
|
||||
TenantScopedConn {
|
||||
mut conn,
|
||||
tenant_id,
|
||||
..
|
||||
}: TenantScopedConn,
|
||||
) -> AppResult<Json<Vec<CorrespondentSummary>>> {
|
||||
let mut conn = state.db()?;
|
||||
|
||||
let correspondents_list: Vec<Correspondent> = correspondents::table
|
||||
.filter(correspondents::tenant_id.eq(tenant_id))
|
||||
.order(correspondents::name.asc())
|
||||
.load(&mut conn)?;
|
||||
|
||||
let usage_rows: Vec<(Uuid, String, i64)> = document_correspondents::table
|
||||
.filter(document_correspondents::tenant_id.eq(tenant_id))
|
||||
.group_by((
|
||||
document_correspondents::correspondent_id,
|
||||
document_correspondents::role,
|
||||
@@ -97,7 +96,11 @@ pub async fn list_correspondents(
|
||||
}
|
||||
|
||||
pub async fn create_correspondent(
|
||||
State(state): State<AppState>,
|
||||
TenantScopedConn {
|
||||
mut conn,
|
||||
tenant_id,
|
||||
..
|
||||
}: TenantScopedConn,
|
||||
Json(payload): Json<CreateCorrespondentRequest>,
|
||||
) -> AppResult<Json<CorrespondentSummary>> {
|
||||
let name = payload.name.trim();
|
||||
@@ -111,9 +114,9 @@ pub async fn create_correspondent(
|
||||
id: new_id,
|
||||
name: name.to_string(),
|
||||
metadata: metadata_value,
|
||||
tenant_id,
|
||||
};
|
||||
|
||||
let mut conn = state.db()?;
|
||||
match diesel::insert_into(correspondents::table)
|
||||
.values(&new_correspondent)
|
||||
.execute(&mut conn)
|
||||
@@ -130,13 +133,17 @@ pub async fn create_correspondent(
|
||||
}
|
||||
|
||||
pub async fn update_correspondent(
|
||||
State(state): State<AppState>,
|
||||
Path(correspondent_id): Path<Uuid>,
|
||||
TenantScopedConn {
|
||||
mut conn,
|
||||
tenant_id,
|
||||
..
|
||||
}: TenantScopedConn,
|
||||
Json(payload): Json<UpdateCorrespondentRequest>,
|
||||
) -> AppResult<Json<CorrespondentSummary>> {
|
||||
let mut conn = state.db()?;
|
||||
let existing: Correspondent = correspondents::table
|
||||
.find(correspondent_id)
|
||||
.filter(correspondents::tenant_id.eq(tenant_id))
|
||||
.first(&mut conn)?;
|
||||
|
||||
let mut new_name: Option<String> = None;
|
||||
@@ -149,6 +156,7 @@ pub async fn update_correspondent(
|
||||
let duplicate = correspondents::table
|
||||
.filter(correspondents::name.eq(trimmed))
|
||||
.filter(correspondents::id.ne(correspondent_id))
|
||||
.filter(correspondents::tenant_id.eq(tenant_id))
|
||||
.first::<Correspondent>(&mut conn)
|
||||
.optional()?;
|
||||
if duplicate.is_some() {
|
||||
@@ -167,7 +175,7 @@ pub async fn update_correspondent(
|
||||
}
|
||||
|
||||
if new_name.is_none() && new_metadata.is_none() {
|
||||
let usage = load_usage_for_correspondent(&mut conn, correspondent_id)?;
|
||||
let usage = load_usage_for_correspondent(&mut conn, tenant_id, correspondent_id)?;
|
||||
return Ok(Json(build_summary(existing.clone(), usage)));
|
||||
}
|
||||
|
||||
@@ -180,24 +188,32 @@ pub async fn update_correspondent(
|
||||
}
|
||||
|
||||
let now = Utc::now().naive_utc();
|
||||
diesel::update(correspondents::table.find(correspondent_id))
|
||||
.set((&changeset, correspondents::updated_at.eq(now)))
|
||||
.execute(&mut conn)?;
|
||||
diesel::update(
|
||||
correspondents::table
|
||||
.find(correspondent_id)
|
||||
.filter(correspondents::tenant_id.eq(tenant_id)),
|
||||
)
|
||||
.set((&changeset, correspondents::updated_at.eq(now)))
|
||||
.execute(&mut conn)?;
|
||||
|
||||
let updated: Correspondent = correspondents::table
|
||||
.find(correspondent_id)
|
||||
.filter(correspondents::tenant_id.eq(tenant_id))
|
||||
.first(&mut conn)?;
|
||||
let usage = load_usage_for_correspondent(&mut conn, correspondent_id)?;
|
||||
let usage = load_usage_for_correspondent(&mut conn, tenant_id, correspondent_id)?;
|
||||
Ok(Json(build_summary(updated, usage)))
|
||||
}
|
||||
|
||||
pub async fn delete_correspondent(
|
||||
State(state): State<AppState>,
|
||||
Path(correspondent_id): Path<Uuid>,
|
||||
TenantScopedConn {
|
||||
mut conn,
|
||||
tenant_id,
|
||||
..
|
||||
}: TenantScopedConn,
|
||||
) -> AppResult<impl IntoResponse> {
|
||||
let mut conn = state.db()?;
|
||||
|
||||
let usage: i64 = document_correspondents::table
|
||||
.filter(document_correspondents::tenant_id.eq(tenant_id))
|
||||
.filter(document_correspondents::correspondent_id.eq(correspondent_id))
|
||||
.select(count_star())
|
||||
.first(&mut conn)?;
|
||||
@@ -208,8 +224,12 @@ pub async fn delete_correspondent(
|
||||
));
|
||||
}
|
||||
|
||||
let deleted =
|
||||
diesel::delete(correspondents::table.find(correspondent_id)).execute(&mut conn)?;
|
||||
let deleted = diesel::delete(
|
||||
correspondents::table
|
||||
.filter(correspondents::id.eq(correspondent_id))
|
||||
.filter(correspondents::tenant_id.eq(tenant_id)),
|
||||
)
|
||||
.execute(&mut conn)?;
|
||||
if deleted == 0 {
|
||||
return Err(AppError::not_found());
|
||||
}
|
||||
@@ -243,10 +263,12 @@ fn normalize_metadata(input: Option<Value>) -> Value {
|
||||
|
||||
fn load_usage_for_correspondent(
|
||||
conn: &mut PgConnection,
|
||||
tenant_id: Uuid,
|
||||
correspondent_id: Uuid,
|
||||
) -> AppResult<BTreeMap<String, i64>> {
|
||||
let rows: Vec<(String, i64)> = document_correspondents::table
|
||||
.filter(document_correspondents::correspondent_id.eq(correspondent_id))
|
||||
.filter(document_correspondents::tenant_id.eq(tenant_id))
|
||||
.group_by(document_correspondents::role)
|
||||
.select((document_correspondents::role, count_star()))
|
||||
.load(conn)?;
|
||||
|
||||
Reference in New Issue
Block a user