use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, EmojiParent, PartialEmoji, User, }; use revolt_models::v0; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; use rocket::{serde::json::Json, State}; use validator::Validate; /// # Edit Emoji /// /// Edit an emoji by its id. #[openapi(tag = "Emojis")] #[patch("/emoji/", data = "")] pub async fn edit_emoji( db: &State, user: User, emoji_id: Reference<'_>, data: Json, ) -> Result> { let data = data.into_inner(); data.validate().map_err(|error| { create_error!(FailedValidation { error: error.to_string() }) })?; let mut emoji = emoji_id.as_emoji(db).await?; match &emoji.parent { EmojiParent::Server { id } => { let server = db.fetch_server(id.as_str()).await?; let mut query = DatabasePermissionQuery::new(db, &user).server(&server); calculate_server_permissions(&mut query) .await .throw_if_lacking_channel_permission(ChannelPermission::ManageCustomisation)?; } EmojiParent::Detached => return Err(create_error!(NotAuthenticated)), } if data.name.is_none() { return Ok(Json(emoji.into())); } let partial = PartialEmoji { name: data.name }; emoji.update(db, partial).await?; Ok(Json(emoji.into())) } #[cfg(test)] mod test { use crate::util::test::TestHarness; use revolt_database::{Emoji, EmojiParent, Member}; use revolt_models::v0; use rocket::http::{ContentType, Header, Status}; use ulid::Ulid; #[rocket::async_test] async fn edit_emoji_name_as_creator() { let harness = TestHarness::new().await; let (_, session, user) = harness.new_user().await; let (server, _) = harness.new_server(&user).await; let emoji_id = Ulid::new().to_string(); let emoji = Emoji { id: emoji_id.clone(), parent: EmojiParent::Server { id: server.id.clone(), }, creator_id: user.id.clone(), name: "initial_name".to_string(), animated: false, nsfw: false, }; emoji.create(&harness.db).await.expect("`Emoji` created"); let response = harness .client .patch(format!("/custom/emoji/{emoji_id}")) .header(Header::new("x-session-token", session.token.to_string())) .header(ContentType::JSON) .body( json!(v0::DataEditEmoji { name: Some("renamed_emoji".to_string()), }) .to_string(), ) .dispatch() .await; assert_eq!(response.status(), Status::Ok); let edited: v0::Emoji = response.into_json().await.expect("`Emoji`"); assert_eq!(edited.name, "renamed_emoji"); } #[rocket::async_test] async fn reject_invalid_emoji_name() { let harness = TestHarness::new().await; let (_, session, user) = harness.new_user().await; let (server, _) = harness.new_server(&user).await; let emoji_id = Ulid::new().to_string(); let emoji = Emoji { id: emoji_id.clone(), parent: EmojiParent::Server { id: server.id.clone(), }, creator_id: user.id.clone(), name: "valid_name".to_string(), animated: false, nsfw: false, }; emoji.create(&harness.db).await.expect("`Emoji` created"); let response = harness .client .patch(format!("/custom/emoji/{emoji_id}")) .header(Header::new("x-session-token", session.token.to_string())) .header(ContentType::JSON) .body( json!(v0::DataEditEmoji { name: Some("Invalid Name".to_string()), }) .to_string(), ) .dispatch() .await; assert_eq!(response.status(), Status::BadRequest); } #[rocket::async_test] async fn reject_edit_for_detached_emoji() { let harness = TestHarness::new().await; let (_, session, user) = harness.new_user().await; let emoji_id = Ulid::new().to_string(); let emoji = Emoji { id: emoji_id.clone(), parent: EmojiParent::Detached, creator_id: user.id.clone(), name: "detached_name".to_string(), animated: false, nsfw: false, }; emoji.create(&harness.db).await.expect("`Emoji` created"); let response = harness .client .patch(format!("/custom/emoji/{emoji_id}")) .header(Header::new("x-session-token", session.token.to_string())) .header(ContentType::JSON) .body( json!(v0::DataEditEmoji { name: Some("should_not_apply".to_string()), }) .to_string(), ) .dispatch() .await; assert_eq!(response.status(), Status::Unauthorized); } #[rocket::async_test] async fn reject_edit_for_creator_without_manage_customisation() { let harness = TestHarness::new().await; let (_, _, owner) = harness.new_user().await; let (_, creator_session, creator) = harness.new_user().await; let (server, _) = harness.new_server(&owner).await; Member::create(&harness.db, &server, &creator, None) .await .expect("`Member` created"); let emoji_id = Ulid::new().to_string(); let emoji = Emoji { id: emoji_id.clone(), parent: EmojiParent::Server { id: server.id.clone(), }, creator_id: creator.id.clone(), name: "member_uploaded_name".to_string(), animated: false, nsfw: false, }; emoji.create(&harness.db).await.expect("`Emoji` created"); let response = harness .client .patch(format!("/custom/emoji/{emoji_id}")) .header(Header::new( "x-session-token", creator_session.token.to_string(), )) .header(ContentType::JSON) .body( json!(v0::DataEditEmoji { name: Some("renamed_without_permission".to_string()), }) .to_string(), ) .dispatch() .await; assert_eq!(response.status(), Status::Forbidden); } }