mirror of
https://github.com/Arcadia-Solutions/arcadia.git
synced 2025-12-30 14:10:06 -06:00
feat: create/edit forum categories in the backend
This commit is contained in:
@@ -90,6 +90,8 @@ use crate::handlers::user_applications::get_user_applications::GetUserApplicatio
|
||||
crate::handlers::torrent_requests::create_torrent_request_comment::exec,
|
||||
crate::handlers::gifts::create_gift::exec,
|
||||
crate::handlers::forum::get_forum::exec,
|
||||
crate::handlers::forum::create_forum_category::exec,
|
||||
crate::handlers::forum::edit_forum_category::exec,
|
||||
crate::handlers::forum::get_forum_sub_category_threads::exec,
|
||||
crate::handlers::forum::get_forum_thread::exec,
|
||||
crate::handlers::forum::get_forum_thread_posts::exec,
|
||||
|
||||
42
backend/api/src/handlers/forum/create_forum_category.rs
Normal file
42
backend/api/src/handlers/forum/create_forum_category.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use crate::{middlewares::auth_middleware::Authdata, Arcadia};
|
||||
use actix_web::{
|
||||
web::{Data, Json},
|
||||
HttpResponse,
|
||||
};
|
||||
use arcadia_common::error::{Error, Result};
|
||||
use arcadia_storage::{
|
||||
models::{
|
||||
forum::{ForumCategory, UserCreatedForumCategory},
|
||||
user::UserClass,
|
||||
},
|
||||
redis::RedisPoolInterface,
|
||||
};
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
operation_id = "Create forum category",
|
||||
tag = "Forum",
|
||||
path = "/api/forum/category",
|
||||
security(
|
||||
("http" = ["Bearer"])
|
||||
),
|
||||
responses(
|
||||
(status = 201, description = "Successfully created the forum category", body=ForumCategory),
|
||||
)
|
||||
)]
|
||||
pub async fn exec<R: RedisPoolInterface + 'static>(
|
||||
forum_category: Json<UserCreatedForumCategory>,
|
||||
arc: Data<Arcadia<R>>,
|
||||
user: Authdata,
|
||||
) -> Result<HttpResponse> {
|
||||
if user.class != UserClass::Staff {
|
||||
return Err(Error::InsufficientPrivileges);
|
||||
}
|
||||
|
||||
let created_category = arc
|
||||
.pool
|
||||
.create_forum_category(&forum_category, user.sub)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Created().json(created_category))
|
||||
}
|
||||
39
backend/api/src/handlers/forum/edit_forum_category.rs
Normal file
39
backend/api/src/handlers/forum/edit_forum_category.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use crate::{middlewares::auth_middleware::Authdata, Arcadia};
|
||||
use actix_web::{
|
||||
web::{Data, Json},
|
||||
HttpResponse,
|
||||
};
|
||||
use arcadia_common::error::{Error, Result};
|
||||
use arcadia_storage::{
|
||||
models::{
|
||||
forum::{EditedForumCategory, ForumCategory},
|
||||
user::UserClass,
|
||||
},
|
||||
redis::RedisPoolInterface,
|
||||
};
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
operation_id = "Edit forum category",
|
||||
tag = "Forum",
|
||||
path = "/api/forum/category",
|
||||
security(
|
||||
("http" = ["Bearer"])
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Successfully edited the forum category", body=ForumCategory),
|
||||
)
|
||||
)]
|
||||
pub async fn exec<R: RedisPoolInterface + 'static>(
|
||||
edited_category: Json<EditedForumCategory>,
|
||||
arc: Data<Arcadia<R>>,
|
||||
user: Authdata,
|
||||
) -> Result<HttpResponse> {
|
||||
if user.class != UserClass::Staff {
|
||||
return Err(Error::InsufficientPrivileges);
|
||||
}
|
||||
|
||||
let updated_category = arc.pool.update_forum_category(&edited_category).await?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(updated_category))
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
pub mod create_forum_category;
|
||||
pub mod create_forum_post;
|
||||
pub mod create_forum_thread;
|
||||
pub mod edit_forum_category;
|
||||
pub mod edit_forum_post;
|
||||
pub mod edit_forum_thread;
|
||||
pub mod get_forum;
|
||||
@@ -12,6 +14,11 @@ use arcadia_storage::redis::RedisPoolInterface;
|
||||
|
||||
pub fn config<R: RedisPoolInterface + 'static>(cfg: &mut ServiceConfig) {
|
||||
cfg.service(resource("").route(get().to(self::get_forum::exec::<R>)));
|
||||
cfg.service(
|
||||
resource("/category")
|
||||
.route(post().to(self::create_forum_category::exec::<R>))
|
||||
.route(put().to(self::edit_forum_category::exec::<R>)),
|
||||
);
|
||||
cfg.service(
|
||||
resource("/thread")
|
||||
.route(get().to(self::get_forum_thread::exec::<R>))
|
||||
|
||||
335
backend/api/tests/test_forum_category.rs
Normal file
335
backend/api/tests/test_forum_category.rs
Normal file
@@ -0,0 +1,335 @@
|
||||
pub mod common;
|
||||
pub mod mocks;
|
||||
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::test;
|
||||
use arcadia_storage::connection_pool::ConnectionPool;
|
||||
use arcadia_storage::models::forum::{
|
||||
EditedForumCategory, ForumCategory, UserCreatedForumCategory,
|
||||
};
|
||||
use common::{auth_header, create_test_app_and_login, TestUser};
|
||||
use mocks::mock_redis::MockRedisPool;
|
||||
use sqlx::PgPool;
|
||||
use std::sync::Arc;
|
||||
|
||||
// ============================================================================
|
||||
// CREATE CATEGORY TESTS
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("with_test_user2"), migrations = "../storage/migrations")]
|
||||
async fn test_staff_can_create_category(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, staff) =
|
||||
create_test_app_and_login(pool, MockRedisPool::default(), 101, 101, TestUser::Staff).await;
|
||||
|
||||
let create_body = UserCreatedForumCategory {
|
||||
name: "New Category".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&staff.token))
|
||||
.set_json(&create_body)
|
||||
.to_request();
|
||||
|
||||
let category: ForumCategory =
|
||||
common::call_and_read_body_json_with_status(&service, req, StatusCode::CREATED).await;
|
||||
|
||||
assert_eq!(category.name, "New Category");
|
||||
assert_eq!(category.created_by_id, 101);
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("with_test_user"), migrations = "../storage/migrations")]
|
||||
async fn test_non_staff_cannot_create_category(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, user) =
|
||||
create_test_app_and_login(pool, MockRedisPool::default(), 100, 100, TestUser::Standard)
|
||||
.await;
|
||||
|
||||
let create_body = UserCreatedForumCategory {
|
||||
name: "New Category".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&user.token))
|
||||
.set_json(&create_body)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("with_test_user"), migrations = "../storage/migrations")]
|
||||
async fn test_create_category_without_auth(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let service = common::create_test_app(
|
||||
pool,
|
||||
MockRedisPool::default(),
|
||||
arcadia_api::OpenSignups::Disabled,
|
||||
100,
|
||||
100,
|
||||
)
|
||||
.await;
|
||||
|
||||
let create_body = UserCreatedForumCategory {
|
||||
name: "New Category".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.set_json(&create_body)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("with_test_user2"), migrations = "../storage/migrations")]
|
||||
async fn test_create_category_with_empty_name(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, staff) =
|
||||
create_test_app_and_login(pool, MockRedisPool::default(), 101, 101, TestUser::Staff).await;
|
||||
|
||||
let create_body = UserCreatedForumCategory { name: "".into() };
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&staff.token))
|
||||
.set_json(&create_body)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("with_test_user2"), migrations = "../storage/migrations")]
|
||||
async fn test_create_category_with_whitespace_only_name(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, staff) =
|
||||
create_test_app_and_login(pool, MockRedisPool::default(), 101, 101, TestUser::Staff).await;
|
||||
|
||||
let create_body = UserCreatedForumCategory { name: " ".into() };
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&staff.token))
|
||||
.set_json(&create_body)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// EDIT CATEGORY TESTS
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(
|
||||
fixtures("with_test_user", "with_test_user2", "with_test_forum_category"),
|
||||
migrations = "../storage/migrations"
|
||||
)]
|
||||
async fn test_staff_can_edit_category(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, staff) =
|
||||
create_test_app_and_login(pool, MockRedisPool::default(), 101, 101, TestUser::Staff).await;
|
||||
|
||||
let edit_body = EditedForumCategory {
|
||||
id: 100,
|
||||
name: "Updated Category Name".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::put()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&staff.token))
|
||||
.set_json(&edit_body)
|
||||
.to_request();
|
||||
|
||||
let category: ForumCategory =
|
||||
common::call_and_read_body_json_with_status(&service, req, StatusCode::OK).await;
|
||||
|
||||
assert_eq!(category.id, 100);
|
||||
assert_eq!(category.name, "Updated Category Name");
|
||||
}
|
||||
|
||||
#[sqlx::test(
|
||||
fixtures("with_test_user", "with_test_forum_category"),
|
||||
migrations = "../storage/migrations"
|
||||
)]
|
||||
async fn test_non_staff_cannot_edit_category(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, user) =
|
||||
create_test_app_and_login(pool, MockRedisPool::default(), 100, 100, TestUser::Standard)
|
||||
.await;
|
||||
|
||||
let edit_body = EditedForumCategory {
|
||||
id: 100,
|
||||
name: "Updated Category Name".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::put()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&user.token))
|
||||
.set_json(&edit_body)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
#[sqlx::test(
|
||||
fixtures("with_test_user", "with_test_forum_category"),
|
||||
migrations = "../storage/migrations"
|
||||
)]
|
||||
async fn test_edit_category_without_auth(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let service = common::create_test_app(
|
||||
pool,
|
||||
MockRedisPool::default(),
|
||||
arcadia_api::OpenSignups::Disabled,
|
||||
100,
|
||||
100,
|
||||
)
|
||||
.await;
|
||||
|
||||
let edit_body = EditedForumCategory {
|
||||
id: 100,
|
||||
name: "Updated Category Name".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::put()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.set_json(&edit_body)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("with_test_user2"), migrations = "../storage/migrations")]
|
||||
async fn test_edit_nonexistent_category(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, staff) =
|
||||
create_test_app_and_login(pool, MockRedisPool::default(), 101, 101, TestUser::Staff).await;
|
||||
|
||||
let edit_body = EditedForumCategory {
|
||||
id: 999,
|
||||
name: "Updated Category Name".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::put()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&staff.token))
|
||||
.set_json(&edit_body)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[sqlx::test(
|
||||
fixtures("with_test_user", "with_test_user2", "with_test_forum_category"),
|
||||
migrations = "../storage/migrations"
|
||||
)]
|
||||
async fn test_edit_category_with_empty_name(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, staff) =
|
||||
create_test_app_and_login(pool, MockRedisPool::default(), 101, 101, TestUser::Staff).await;
|
||||
|
||||
let edit_body = EditedForumCategory {
|
||||
id: 100,
|
||||
name: "".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::put()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&staff.token))
|
||||
.set_json(&edit_body)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
#[sqlx::test(
|
||||
fixtures("with_test_user", "with_test_user2", "with_test_forum_category"),
|
||||
migrations = "../storage/migrations"
|
||||
)]
|
||||
async fn test_edit_category_with_whitespace_only_name(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, staff) =
|
||||
create_test_app_and_login(pool, MockRedisPool::default(), 101, 101, TestUser::Staff).await;
|
||||
|
||||
let edit_body = EditedForumCategory {
|
||||
id: 100,
|
||||
name: " ".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::put()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&staff.token))
|
||||
.set_json(&edit_body)
|
||||
.to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// INTEGRATION TESTS
|
||||
// ============================================================================
|
||||
|
||||
#[sqlx::test(fixtures("with_test_user2"), migrations = "../storage/migrations")]
|
||||
async fn test_create_and_edit_category_flow(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, staff) =
|
||||
create_test_app_and_login(pool, MockRedisPool::default(), 101, 101, TestUser::Staff).await;
|
||||
|
||||
// Create a category
|
||||
let create_body = UserCreatedForumCategory {
|
||||
name: "Initial Category".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&staff.token))
|
||||
.set_json(&create_body)
|
||||
.to_request();
|
||||
|
||||
let category: ForumCategory =
|
||||
common::call_and_read_body_json_with_status(&service, req, StatusCode::CREATED).await;
|
||||
let category_id = category.id;
|
||||
|
||||
assert_eq!(category.name, "Initial Category");
|
||||
|
||||
// Edit the category
|
||||
let edit_body = EditedForumCategory {
|
||||
id: category_id,
|
||||
name: "Edited Category".into(),
|
||||
};
|
||||
|
||||
let req = test::TestRequest::put()
|
||||
.uri("/api/forum/category")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&staff.token))
|
||||
.set_json(&edit_body)
|
||||
.to_request();
|
||||
|
||||
let edited_category: ForumCategory =
|
||||
common::call_and_read_body_json_with_status(&service, req, StatusCode::OK).await;
|
||||
|
||||
assert_eq!(edited_category.id, category_id);
|
||||
assert_eq!(edited_category.name, "Edited Category");
|
||||
}
|
||||
@@ -258,6 +258,18 @@ pub enum Error {
|
||||
#[error("could not search forum threads")]
|
||||
CouldNotSearchForumThreads(#[source] sqlx::Error),
|
||||
|
||||
#[error("could not create forum category")]
|
||||
CouldNotCreateForumCategory(#[source] sqlx::Error),
|
||||
|
||||
#[error("could not update forum category")]
|
||||
CouldNotUpdateForumCategory(#[source] sqlx::Error),
|
||||
|
||||
#[error("forum category not found")]
|
||||
ForumCategoryNotFound,
|
||||
|
||||
#[error("forum category name cannot be empty")]
|
||||
ForumCategoryNameEmpty,
|
||||
|
||||
#[error("insufficient privileges")]
|
||||
InsufficientPrivileges,
|
||||
|
||||
@@ -358,7 +370,8 @@ impl actix_web::ResponseError for Error {
|
||||
| Error::TorrentFileInvalid
|
||||
| Error::InvalidUserIdOrTorrentId
|
||||
| Error::ForumThreadNameEmpty
|
||||
| Error::ForumPostEmpty => StatusCode::BAD_REQUEST,
|
||||
| Error::ForumPostEmpty
|
||||
| Error::ForumCategoryNameEmpty => StatusCode::BAD_REQUEST,
|
||||
|
||||
// 401 Unauthorized
|
||||
Error::InvalidOrExpiredRefreshToken | Error::InvalidatedToken => {
|
||||
@@ -380,7 +393,8 @@ impl actix_web::ResponseError for Error {
|
||||
| Error::CouldNotFindTitleGroupComment(_)
|
||||
| Error::CouldNotFindForumThread(_)
|
||||
| Error::CouldNotFindForumSubCategory(_)
|
||||
| Error::CssSheetNotFound(_) => StatusCode::NOT_FOUND,
|
||||
| Error::CssSheetNotFound(_)
|
||||
| Error::ForumCategoryNotFound => StatusCode::NOT_FOUND,
|
||||
|
||||
// 409 Conflict
|
||||
Error::NoInvitationsAvailable
|
||||
|
||||
41
backend/storage/.sqlx/query-2888cad14797fcf3bf4f198b2f01076b4d105b7c6abdb63159c8cf5241ed6903.json
generated
Normal file
41
backend/storage/.sqlx/query-2888cad14797fcf3bf4f198b2f01076b4d105b7c6abdb63159c8cf5241ed6903.json
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE forum_categories\n SET name = $1\n WHERE id = $2\n RETURNING *\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_by_id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "2888cad14797fcf3bf4f198b2f01076b4d105b7c6abdb63159c8cf5241ed6903"
|
||||
}
|
||||
41
backend/storage/.sqlx/query-4646106c53b4d1dbb1946e0bc789e0f6a7d9c8757371b80af6f813e9bef8eeee.json
generated
Normal file
41
backend/storage/.sqlx/query-4646106c53b4d1dbb1946e0bc789e0f6a7d9c8757371b80af6f813e9bef8eeee.json
generated
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO forum_categories (name, created_by_id)\n VALUES ($1, $2)\n RETURNING *\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "created_by_id",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int4"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4646106c53b4d1dbb1946e0bc789e0f6a7d9c8757371b80af6f813e9bef8eeee"
|
||||
}
|
||||
@@ -14,6 +14,17 @@ pub struct ForumCategory {
|
||||
pub created_by_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, ToSchema)]
|
||||
pub struct UserCreatedForumCategory {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, ToSchema)]
|
||||
pub struct EditedForumCategory {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, FromRow, ToSchema)]
|
||||
pub struct ForumSubCategory {
|
||||
pub id: i32,
|
||||
|
||||
@@ -3,11 +3,11 @@ use crate::{
|
||||
models::{
|
||||
common::PaginatedResults,
|
||||
forum::{
|
||||
EditedForumPost, EditedForumThread, ForumCategoryHierarchy, ForumCategoryLite,
|
||||
ForumPost, ForumPostAndThreadName, ForumPostHierarchy, ForumSearchQuery,
|
||||
ForumSearchResult, ForumSubCategoryHierarchy, ForumThread, ForumThreadEnriched,
|
||||
ForumThreadPostLite, GetForumThreadPostsQuery, UserCreatedForumPost,
|
||||
UserCreatedForumThread,
|
||||
EditedForumCategory, EditedForumPost, EditedForumThread, ForumCategory,
|
||||
ForumCategoryHierarchy, ForumCategoryLite, ForumPost, ForumPostAndThreadName,
|
||||
ForumPostHierarchy, ForumSearchQuery, ForumSearchResult, ForumSubCategoryHierarchy,
|
||||
ForumThread, ForumThreadEnriched, ForumThreadPostLite, GetForumThreadPostsQuery,
|
||||
UserCreatedForumCategory, UserCreatedForumPost, UserCreatedForumThread,
|
||||
},
|
||||
user::{UserLite, UserLiteAvatar},
|
||||
},
|
||||
@@ -734,4 +734,59 @@ impl ConnectionPool {
|
||||
page_size: form.page_size,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn create_forum_category(
|
||||
&self,
|
||||
forum_category: &UserCreatedForumCategory,
|
||||
current_user_id: i32,
|
||||
) -> Result<ForumCategory> {
|
||||
if forum_category.name.trim().is_empty() {
|
||||
return Err(Error::ForumCategoryNameEmpty);
|
||||
}
|
||||
|
||||
let created_category = sqlx::query_as!(
|
||||
ForumCategory,
|
||||
r#"
|
||||
INSERT INTO forum_categories (name, created_by_id)
|
||||
VALUES ($1, $2)
|
||||
RETURNING *
|
||||
"#,
|
||||
forum_category.name,
|
||||
current_user_id
|
||||
)
|
||||
.fetch_one(self.borrow())
|
||||
.await
|
||||
.map_err(Error::CouldNotCreateForumCategory)?;
|
||||
|
||||
Ok(created_category)
|
||||
}
|
||||
|
||||
pub async fn update_forum_category(
|
||||
&self,
|
||||
edited_category: &EditedForumCategory,
|
||||
) -> Result<ForumCategory> {
|
||||
if edited_category.name.trim().is_empty() {
|
||||
return Err(Error::ForumCategoryNameEmpty);
|
||||
}
|
||||
|
||||
let updated_category = sqlx::query_as!(
|
||||
ForumCategory,
|
||||
r#"
|
||||
UPDATE forum_categories
|
||||
SET name = $1
|
||||
WHERE id = $2
|
||||
RETURNING *
|
||||
"#,
|
||||
edited_category.name,
|
||||
edited_category.id
|
||||
)
|
||||
.fetch_one(self.borrow())
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
sqlx::Error::RowNotFound => Error::ForumCategoryNotFound,
|
||||
_ => Error::CouldNotUpdateForumCategory(e),
|
||||
})?;
|
||||
|
||||
Ok(updated_category)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user