feat: migrate file storage to postgres with sqlx
This commit is contained in:
@@ -7,53 +7,53 @@ use tracing::info;
|
||||
|
||||
use crate::{
|
||||
error::LoftError,
|
||||
model::{File, FileController, FileToCreate},
|
||||
model::{FileRecord, FileRepository, FileToCreate},
|
||||
};
|
||||
|
||||
pub fn routes_file(file_controller: FileController) -> Router {
|
||||
pub fn routes_file(file_repository: FileRepository) -> Router {
|
||||
Router::new()
|
||||
.route("/files", get(list_files).post(upload_file))
|
||||
.route("/files/{id}", get(download_file).delete(delete_file))
|
||||
.with_state(file_controller)
|
||||
.with_state(file_repository)
|
||||
}
|
||||
|
||||
async fn upload_file(
|
||||
State(file_controller): State<FileController>,
|
||||
State(file_repository): State<FileRepository>,
|
||||
Json(file_to_create): Json<FileToCreate>,
|
||||
) -> Result<Json<File>, LoftError> {
|
||||
) -> Result<Json<FileRecord>, LoftError> {
|
||||
info!("handler: upload_file");
|
||||
|
||||
let file = file_controller.upload_file(file_to_create).await?;
|
||||
let file = file_repository.upload_file(file_to_create).await?;
|
||||
Ok(Json(file))
|
||||
}
|
||||
|
||||
async fn download_file(
|
||||
State(file_controller): State<FileController>,
|
||||
State(file_repository): State<FileRepository>,
|
||||
Path(file_id): Path<u64>,
|
||||
) -> Result<Json<File>, LoftError> {
|
||||
) -> Result<Json<FileRecord>, LoftError> {
|
||||
info!("handler: download_file");
|
||||
|
||||
let file = file_controller.download_file(file_id).await?;
|
||||
let file = file_repository.download_file(file_id as i64).await?;
|
||||
Ok(Json(file))
|
||||
}
|
||||
|
||||
async fn delete_file(
|
||||
State(file_controller): State<FileController>,
|
||||
State(file_repository): State<FileRepository>,
|
||||
Path(file_id): Path<u64>,
|
||||
) -> Result<Json<File>, LoftError> {
|
||||
) -> Result<Json<FileRecord>, LoftError> {
|
||||
info!("handler: delete_file");
|
||||
|
||||
let file = file_controller.delete_file(file_id).await?;
|
||||
let file = file_repository.delete_file(file_id as i64).await?;
|
||||
Ok(Json(file))
|
||||
}
|
||||
|
||||
async fn list_files(
|
||||
State(file_controller): State<FileController>,
|
||||
State(file_repository): State<FileRepository>,
|
||||
// can add a filters param here
|
||||
) -> Result<Json<Vec<File>>, LoftError> {
|
||||
) -> Result<Json<Vec<FileRecord>>, LoftError> {
|
||||
info!("handler: list_files");
|
||||
|
||||
let files = file_controller.list_files().await?;
|
||||
let files = file_repository.list_files().await?;
|
||||
Ok(Json(files))
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ mod tests {
|
||||
use tower_cookies::CookieManagerLayer;
|
||||
|
||||
use crate::{
|
||||
model::FileController,
|
||||
model::FileRepository,
|
||||
web::{
|
||||
mw_auth::{mw_ctx_resolver, mw_require_auth},
|
||||
routes_file::routes_file,
|
||||
@@ -77,13 +77,13 @@ mod tests {
|
||||
const BAD_AUTH_COOKIE: &str = "auth-token=user-1.0123456789";
|
||||
|
||||
async fn test_server() -> TestServer {
|
||||
let file_controller = FileController::new().await.unwrap();
|
||||
let file_repository = FileRepository::new().await.unwrap();
|
||||
let routes_file =
|
||||
routes_file(file_controller.clone()).route_layer(middleware::from_fn(mw_require_auth));
|
||||
routes_file(file_repository.clone()).route_layer(middleware::from_fn(mw_require_auth));
|
||||
let app = Router::new()
|
||||
.nest("/api", routes_file)
|
||||
.layer(middleware::from_fn_with_state(
|
||||
file_controller,
|
||||
file_repository,
|
||||
mw_ctx_resolver,
|
||||
))
|
||||
.layer(CookieManagerLayer::new());
|
||||
@@ -111,13 +111,20 @@ mod tests {
|
||||
let server = test_server().await;
|
||||
server
|
||||
.post("/api/files")
|
||||
.json(&json!({"name": "a.txt", "file_type": "text"}))
|
||||
.json(&json!({"name": "a", "file_type": "Document"}))
|
||||
.await
|
||||
.assert_status_unauthorized();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial_test::serial]
|
||||
async fn test_list_files_empty() {
|
||||
let file_repository = FileRepository::new().await.unwrap();
|
||||
sqlx::query!("TRUNCATE TABLE file_records")
|
||||
.execute(&file_repository.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let server = test_server().await;
|
||||
server
|
||||
.get("/api/files")
|
||||
@@ -128,17 +135,25 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial_test::serial]
|
||||
async fn test_upload_and_list_files() {
|
||||
let file_repository = FileRepository::new().await.unwrap();
|
||||
sqlx::query!("TRUNCATE TABLE file_records")
|
||||
.execute(&file_repository.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let server = test_server().await;
|
||||
let res = server
|
||||
.post("/api/files")
|
||||
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
|
||||
.json(&json!({"name": "a.txt", "file_type": "text"}))
|
||||
.json(&json!({"name": "a", "file_type": "Document", "size": 10}))
|
||||
.await;
|
||||
res.assert_status_ok();
|
||||
let file = res.json::<serde_json::Value>();
|
||||
assert_eq!(file["name"], "a.txt");
|
||||
assert_eq!(file["id"], 0);
|
||||
assert_eq!(file["name"], "a");
|
||||
assert_eq!(file["file_type"], "Document");
|
||||
assert_eq!(file["size"], 10);
|
||||
|
||||
let list = server
|
||||
.get("/api/files")
|
||||
@@ -149,22 +164,25 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial_test::serial]
|
||||
async fn test_download_file() {
|
||||
let server = test_server().await;
|
||||
server
|
||||
let post_res = server
|
||||
.post("/api/files")
|
||||
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
|
||||
.json(&json!({"name": "b.txt", "file_type": "text"}))
|
||||
.json(&json!({"name": "b", "file_type": "Document", "size": 10}))
|
||||
.await;
|
||||
let id = post_res.json::<serde_json::Value>()["id"].as_i64().unwrap();
|
||||
let res = server
|
||||
.get("/api/files/0")
|
||||
.get(&format!("/api/files/{id}"))
|
||||
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
|
||||
.await;
|
||||
res.assert_status_ok();
|
||||
assert_eq!(res.json::<serde_json::Value>()["name"], "b.txt");
|
||||
assert_eq!(res.json::<serde_json::Value>()["name"], "b");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial_test::serial]
|
||||
async fn test_download_file_not_found() {
|
||||
let server = test_server().await;
|
||||
server
|
||||
@@ -175,26 +193,31 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial_test::serial]
|
||||
async fn test_delete_file() {
|
||||
let server = test_server().await;
|
||||
server
|
||||
let post_res = server
|
||||
.post("/api/files")
|
||||
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
|
||||
.json(&json!({"name": "c.txt", "file_type": "text"}))
|
||||
.json(&json!({"name": "c", "file_type": "Document", "size": 10}))
|
||||
.await;
|
||||
let id = post_res.json::<serde_json::Value>()["id"].as_i64().unwrap();
|
||||
|
||||
server
|
||||
.delete("/api/files/0")
|
||||
.delete(&format!("/api/files/{id}"))
|
||||
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
|
||||
.await
|
||||
.assert_status_ok();
|
||||
|
||||
server
|
||||
.get("/api/files/0")
|
||||
.get(&format!("/api/files/{id}"))
|
||||
.add_header(axum::http::header::COOKIE, AUTH_COOKIE)
|
||||
.await
|
||||
.assert_status_not_found();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial_test::serial]
|
||||
async fn test_delete_file_not_found() {
|
||||
let server = test_server().await;
|
||||
server
|
||||
|
||||
Reference in New Issue
Block a user