forked from jmug/stoatchat
refactor: use fixtures to populate db
refactor: method for creating server channels
This commit is contained in:
89
crates/core/database/fixtures/server_with_roles.json
Normal file
89
crates/core/database/fixtures/server_with_roles.json
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"_object_type": "User",
|
||||||
|
"_id": "__ID:0__",
|
||||||
|
"username": "Owner",
|
||||||
|
"discriminator": "0001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "User",
|
||||||
|
"_id": "__ID:1__",
|
||||||
|
"username": "Moderator",
|
||||||
|
"discriminator": "0001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "User",
|
||||||
|
"_id": "__ID:2__",
|
||||||
|
"username": "User",
|
||||||
|
"discriminator": "0001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "Channel",
|
||||||
|
"_id": "__ID:3__",
|
||||||
|
"channel_type": "TextChannel",
|
||||||
|
"name": "General",
|
||||||
|
"server": "__ID:4__",
|
||||||
|
"default_permissions": {
|
||||||
|
"a": 0,
|
||||||
|
"d": 1048576
|
||||||
|
},
|
||||||
|
"role_permissions": {
|
||||||
|
"__ID:5__": {
|
||||||
|
"a": 1048576,
|
||||||
|
"d": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "Server",
|
||||||
|
"_id": "__ID:4__",
|
||||||
|
"owner": "__ID:0__",
|
||||||
|
"name": "Server",
|
||||||
|
"channels": ["__ID:3__"],
|
||||||
|
"roles": {
|
||||||
|
"__ID:5__": {
|
||||||
|
"name": "Moderator",
|
||||||
|
"permissions": {
|
||||||
|
"a": 545270208,
|
||||||
|
"d": 0
|
||||||
|
},
|
||||||
|
"rank": 3
|
||||||
|
},
|
||||||
|
"__ID:6__": {
|
||||||
|
"name": "Owner",
|
||||||
|
"permissions": {
|
||||||
|
"a": 0,
|
||||||
|
"d": 0
|
||||||
|
},
|
||||||
|
"rank": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default_permissions": 4000322560
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "ServerMember",
|
||||||
|
"_id": {
|
||||||
|
"user": "__ID:0__",
|
||||||
|
"server": "__ID:4__"
|
||||||
|
},
|
||||||
|
"roles": ["__ID:6__"],
|
||||||
|
"joined_at": 1698318340195
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "ServerMember",
|
||||||
|
"_id": {
|
||||||
|
"user": "__ID:1__",
|
||||||
|
"server": "__ID:4__"
|
||||||
|
},
|
||||||
|
"roles": ["__ID:5__"],
|
||||||
|
"joined_at": 1698318340195
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_object_type": "ServerMember",
|
||||||
|
"_id": {
|
||||||
|
"user": "__ID:2__",
|
||||||
|
"server": "__ID:4__"
|
||||||
|
},
|
||||||
|
"joined_at": 1698318340195
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -6,7 +6,10 @@ use revolt_permissions::OverrideField;
|
|||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{events::client::EventV1, Database, File, IntoDocumentPath, SystemMessage, User};
|
use crate::{
|
||||||
|
events::client::EventV1, Database, File, IntoDocumentPath, PartialServer, Server,
|
||||||
|
SystemMessage, User,
|
||||||
|
};
|
||||||
|
|
||||||
auto_derived!(
|
auto_derived!(
|
||||||
#[serde(tag = "channel_type")]
|
#[serde(tag = "channel_type")]
|
||||||
@@ -189,6 +192,67 @@ impl Channel {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
/// Create a new server channel
|
||||||
|
pub async fn create_server_channel(
|
||||||
|
db: &Database,
|
||||||
|
server: &mut Server,
|
||||||
|
data: v0::DataCreateServerChannel,
|
||||||
|
update_server: bool,
|
||||||
|
) -> Result<Channel> {
|
||||||
|
let config = config().await;
|
||||||
|
if server.channels.len() > config.features.limits.default.server_channels {
|
||||||
|
return Err(create_error!(TooManyChannels {
|
||||||
|
max: config.features.limits.default.server_channels,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = ulid::Ulid::new().to_string();
|
||||||
|
let channel = match data.channel_type {
|
||||||
|
v0::LegacyServerChannelType::Text => Channel::TextChannel {
|
||||||
|
id: id.clone(),
|
||||||
|
server: server.id.to_owned(),
|
||||||
|
name: data.name,
|
||||||
|
description: data.description,
|
||||||
|
icon: None,
|
||||||
|
last_message_id: None,
|
||||||
|
default_permissions: None,
|
||||||
|
role_permissions: HashMap::new(),
|
||||||
|
nsfw: data.nsfw.unwrap_or(false),
|
||||||
|
},
|
||||||
|
v0::LegacyServerChannelType::Voice => Channel::VoiceChannel {
|
||||||
|
id: id.clone(),
|
||||||
|
server: server.id.to_owned(),
|
||||||
|
name: data.name,
|
||||||
|
description: data.description,
|
||||||
|
icon: None,
|
||||||
|
default_permissions: None,
|
||||||
|
role_permissions: HashMap::new(),
|
||||||
|
nsfw: data.nsfw.unwrap_or(false),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
db.insert_channel(&channel).await?;
|
||||||
|
|
||||||
|
if update_server {
|
||||||
|
server
|
||||||
|
.update(
|
||||||
|
db,
|
||||||
|
PartialServer {
|
||||||
|
channels: Some([server.channels.clone(), [id].into()].concat()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
EventV1::ChannelCreate(channel.clone().into())
|
||||||
|
.p(server.id.clone())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(channel)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a group
|
/// Create a group
|
||||||
pub async fn create_group(
|
pub async fn create_group(
|
||||||
db: &Database,
|
db: &Database,
|
||||||
@@ -627,130 +691,18 @@ impl IntoDocumentPath for FieldsChannel {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::HashMap;
|
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
||||||
|
|
||||||
use revolt_permissions::{calculate_channel_permissions, ChannelPermission, OverrideField};
|
use crate::{fixture, util::permissions::DatabasePermissionQuery};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
util::permissions::DatabasePermissionQuery, Channel, Member, MemberCompositeKey, Role,
|
|
||||||
Server, User,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn permissions_text_channel() {
|
async fn permissions_text_channel() {
|
||||||
database_test!(|db| async move {
|
database_test!(|db| async move {
|
||||||
let owner = User::create(&db, "Owner".to_string(), None, None)
|
fixture!(db, "server_with_roles",
|
||||||
.await
|
owner user 0
|
||||||
.unwrap();
|
moderator user 1
|
||||||
|
user user 2
|
||||||
let moderator = User::create(&db, "Moderator".to_string(), None, None)
|
channel channel 3);
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let user = User::create(&db, "User".to_string(), None, None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let server_id = ulid::Ulid::new().to_string();
|
|
||||||
|
|
||||||
let channel = Channel::TextChannel {
|
|
||||||
id: ulid::Ulid::new().to_string(),
|
|
||||||
server: server_id.clone(),
|
|
||||||
name: "Channel".to_string(),
|
|
||||||
description: None,
|
|
||||||
icon: None,
|
|
||||||
last_message_id: None,
|
|
||||||
default_permissions: Some(OverrideField {
|
|
||||||
d: 1048576, // TODO: bitfield
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
role_permissions: HashMap::from([(
|
|
||||||
"01F9HFTSBWTNA2F4TMSV7VM3FG".to_string(),
|
|
||||||
OverrideField {
|
|
||||||
a: 1048576, // TODO: bitfield
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)]),
|
|
||||||
nsfw: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let server = Server {
|
|
||||||
id: server_id,
|
|
||||||
owner: owner.id.clone(),
|
|
||||||
name: "My Server".to_string(),
|
|
||||||
description: None,
|
|
||||||
channels: vec![channel.id()],
|
|
||||||
categories: None,
|
|
||||||
system_messages: None,
|
|
||||||
roles: HashMap::from([
|
|
||||||
(
|
|
||||||
"01F9HFTSBWTNA2F4TMSV7VM3FG".to_string(),
|
|
||||||
Role {
|
|
||||||
name: "Moderator".to_string(),
|
|
||||||
permissions: OverrideField {
|
|
||||||
a: 545270208, // TODO: explicit
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
colour: None,
|
|
||||||
hoist: true,
|
|
||||||
rank: 3,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"01FBF9DNHSRPVTWFMNB3JNB8FK".to_string(),
|
|
||||||
Role {
|
|
||||||
name: "Owner".to_string(),
|
|
||||||
permissions: Default::default(),
|
|
||||||
colour: None,
|
|
||||||
hoist: true,
|
|
||||||
rank: 0,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
default_permissions: 4000322560, // TODO: use bitfield
|
|
||||||
icon: None,
|
|
||||||
banner: None,
|
|
||||||
flags: None,
|
|
||||||
nsfw: false,
|
|
||||||
analytics: false,
|
|
||||||
discoverable: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: proper creation
|
|
||||||
db.insert_channel(&channel).await.unwrap();
|
|
||||||
server.create(&db).await.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: owner.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
roles: vec!["01FBF9DNHSRPVTWFMNB3JNB8FK".to_string()],
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: moderator.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
roles: vec!["01F9HFTSBWTNA2F4TMSV7VM3FG".to_string()],
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: user.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut query = DatabasePermissionQuery::new(&db, &owner).channel(&channel);
|
let mut query = DatabasePermissionQuery::new(&db, &owner).channel(&channel);
|
||||||
assert!(calculate_channel_permissions(&mut query)
|
assert!(calculate_channel_permissions(&mut query)
|
||||||
|
|||||||
@@ -453,109 +453,18 @@ impl SystemMessageChannels {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::HashMap;
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
|
|
||||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission, OverrideField};
|
use crate::{fixture, util::permissions::DatabasePermissionQuery};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
util::permissions::DatabasePermissionQuery, Member, MemberCompositeKey, Role, Server, User,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn permissions() {
|
async fn permissions() {
|
||||||
database_test!(|db| async move {
|
database_test!(|db| async move {
|
||||||
let owner = User::create(&db, "Owner".to_string(), None, None)
|
fixture!(db, "server_with_roles",
|
||||||
.await
|
owner user 0
|
||||||
.unwrap();
|
moderator user 1
|
||||||
|
user user 2
|
||||||
let moderator = User::create(&db, "Moderator".to_string(), None, None)
|
server server 4);
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let user = User::create(&db, "User".to_string(), None, None)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let server_id = ulid::Ulid::new().to_string();
|
|
||||||
|
|
||||||
// TODO: seeder functions
|
|
||||||
// e.g. seed!("channel", "file.json")
|
|
||||||
let server = Server {
|
|
||||||
id: server_id,
|
|
||||||
owner: owner.id.clone(),
|
|
||||||
name: "My Server".to_string(),
|
|
||||||
description: None,
|
|
||||||
channels: vec![],
|
|
||||||
categories: None,
|
|
||||||
system_messages: None,
|
|
||||||
roles: HashMap::from([
|
|
||||||
(
|
|
||||||
"01F9HFTSBWTNA2F4TMSV7VM3FG".to_string(),
|
|
||||||
Role {
|
|
||||||
name: "Moderator".to_string(),
|
|
||||||
permissions: OverrideField {
|
|
||||||
a: 545270208, // TODO: explicit
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
colour: None,
|
|
||||||
hoist: true,
|
|
||||||
rank: 3,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"01FBF9DNHSRPVTWFMNB3JNB8FK".to_string(),
|
|
||||||
Role {
|
|
||||||
name: "Owner".to_string(),
|
|
||||||
permissions: Default::default(),
|
|
||||||
colour: None,
|
|
||||||
hoist: true,
|
|
||||||
rank: 0,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
default_permissions: 4000322560, // TODO: use bitfield
|
|
||||||
icon: None,
|
|
||||||
banner: None,
|
|
||||||
flags: None,
|
|
||||||
nsfw: false,
|
|
||||||
analytics: false,
|
|
||||||
discoverable: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: proper creation
|
|
||||||
server.create(&db).await.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: owner.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
roles: vec!["01FBF9DNHSRPVTWFMNB3JNB8FK".to_string()],
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: moderator.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
roles: vec!["01F9HFTSBWTNA2F4TMSV7VM3FG".to_string()],
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
db.insert_member(&Member {
|
|
||||||
id: MemberCompositeKey {
|
|
||||||
user: user.id.clone(),
|
|
||||||
server: server.id.clone(),
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut query = DatabasePermissionQuery::new(&db, &owner).server(&server);
|
let mut query = DatabasePermissionQuery::new(&db, &owner).server(&server);
|
||||||
assert!(calculate_server_permissions(&mut query)
|
assert!(calculate_server_permissions(&mut query)
|
||||||
|
|||||||
@@ -2,3 +2,6 @@ pub mod bridge;
|
|||||||
pub mod idempotency;
|
pub mod idempotency;
|
||||||
pub mod permissions;
|
pub mod permissions;
|
||||||
pub mod reference;
|
pub mod reference;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod test_fixtures;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use schemars::{
|
|||||||
JsonSchema,
|
JsonSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Bot, Channel, Database, Emoji, Message, User, Webhook};
|
use crate::{Bot, Channel, Database, Emoji, Message, Server, User, Webhook};
|
||||||
|
|
||||||
/// Reference to some object in the database
|
/// Reference to some object in the database
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@@ -42,6 +42,11 @@ impl Reference {
|
|||||||
db.fetch_message(&self.id).await
|
db.fetch_message(&self.id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch server from Ref
|
||||||
|
pub async fn as_server(&self, db: &Database) -> Result<Server> {
|
||||||
|
db.fetch_server(&self.id).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Fetch user from Ref
|
/// Fetch user from Ref
|
||||||
pub async fn as_user(&self, db: &Database) -> Result<User> {
|
pub async fn as_user(&self, db: &Database) -> Result<User> {
|
||||||
db.fetch_user(&self.id).await
|
db.fetch_user(&self.id).await
|
||||||
|
|||||||
103
crates/core/database/src/util/test_fixtures.rs
Normal file
103
crates/core/database/src/util/test_fixtures.rs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::{Captures, Regex};
|
||||||
|
use serde_json::from_str;
|
||||||
|
|
||||||
|
use crate::{Channel, Database, Member, Server, User};
|
||||||
|
|
||||||
|
static RE_ID: Lazy<Regex> = Lazy::new(|| Regex::new("__ID:(\\d+)__").unwrap());
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(tag = "_object_type")]
|
||||||
|
enum LoadedFixture {
|
||||||
|
User(User),
|
||||||
|
Channel(Channel),
|
||||||
|
Server(Server),
|
||||||
|
ServerMember(Member),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load_fixture(db: &Database, input: &str) -> HashMap<String, String> {
|
||||||
|
let mut ids = HashMap::<String, String>::new();
|
||||||
|
let input = RE_ID.replace_all(input, |cap: &Captures| {
|
||||||
|
let d = cap.get(1).unwrap().as_str();
|
||||||
|
|
||||||
|
if !ids.contains_key(d) {
|
||||||
|
ids.insert(d.to_string(), ulid::Ulid::new().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
ids.get(d).unwrap().clone()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deserialise the fixtures
|
||||||
|
let items: Vec<LoadedFixture> = from_str(&input).expect("Failed to deserialise fixture");
|
||||||
|
|
||||||
|
// Load all of the items within
|
||||||
|
for item in items {
|
||||||
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
match item {
|
||||||
|
LoadedFixture::User(user) => db.insert_user(&user).await.unwrap(),
|
||||||
|
LoadedFixture::Channel(channel) => db.insert_channel(&channel).await.unwrap(),
|
||||||
|
LoadedFixture::Server(server) => db.insert_server(&server).await.unwrap(),
|
||||||
|
LoadedFixture::ServerMember(member) => db.insert_member(&member).await.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return IDs for ease of use
|
||||||
|
ids
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait FetchFixture {
|
||||||
|
async fn user(&self, db: &Database, d: usize) -> User;
|
||||||
|
async fn channel(&self, db: &Database, d: usize) -> Channel;
|
||||||
|
async fn server(&self, db: &Database, d: usize) -> Server;
|
||||||
|
async fn member(&self, db: &Database, d_server: usize, d_user: usize) -> Member;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl FetchFixture for HashMap<String, String> {
|
||||||
|
async fn user(&self, db: &Database, d: usize) -> User {
|
||||||
|
db.fetch_user(self.get(&d.to_string()).unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn channel(&self, db: &Database, d: usize) -> Channel {
|
||||||
|
db.fetch_channel(self.get(&d.to_string()).unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn server(&self, db: &Database, d: usize) -> Server {
|
||||||
|
db.fetch_server(self.get(&d.to_string()).unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn member(&self, db: &Database, d_server: usize, d_user: usize) -> Member {
|
||||||
|
db.fetch_member(
|
||||||
|
self.get(&d_server.to_string()).unwrap(),
|
||||||
|
self.get(&d_user.to_string()).unwrap(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! fixture {
|
||||||
|
( $database:expr, $name:expr, $( $variable:ident $type:ident $id: expr )+ ) => {
|
||||||
|
use $crate::util::test_fixtures::FetchFixture;
|
||||||
|
|
||||||
|
let fixtures = $crate::util::test_fixtures::load_fixture(
|
||||||
|
&$database,
|
||||||
|
include_str!(concat!("../../../fixtures/", $name, ".json")),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
$(
|
||||||
|
let $variable = fixtures.$type(&$database, $id).await;
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -212,24 +212,52 @@ auto_derived!(
|
|||||||
#[cfg_attr(feature = "validator", derive(validator::Validate))]
|
#[cfg_attr(feature = "validator", derive(validator::Validate))]
|
||||||
pub struct DataCreateGroup {
|
pub struct DataCreateGroup {
|
||||||
/// Group name
|
/// Group name
|
||||||
#[validate(length(min = 1, max = 32))]
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Group description
|
/// Group description
|
||||||
#[validate(length(min = 0, max = 1024))]
|
#[cfg_attr(feature = "validator", validate(length(min = 0, max = 1024)))]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// Group icon
|
/// Group icon
|
||||||
#[validate(length(min = 1, max = 128))]
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))]
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
/// Array of user IDs to add to the group
|
/// Array of user IDs to add to the group
|
||||||
///
|
///
|
||||||
/// Must be friends with these users.
|
/// Must be friends with these users.
|
||||||
#[validate(length(min = 0, max = 49))]
|
#[cfg_attr(feature = "validator", validate(length(min = 0, max = 49)))]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub users: HashSet<String>,
|
pub users: HashSet<String>,
|
||||||
/// Whether this group is age-restricted
|
/// Whether this group is age-restricted
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub nsfw: Option<bool>,
|
pub nsfw: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Server Channel Type
|
||||||
|
#[derive(Default)]
|
||||||
|
pub enum LegacyServerChannelType {
|
||||||
|
/// Text Channel
|
||||||
|
#[default]
|
||||||
|
Text,
|
||||||
|
/// Voice Channel
|
||||||
|
Voice,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create new server channel
|
||||||
|
#[derive(Default)]
|
||||||
|
#[cfg_attr(feature = "validator", derive(validator::Validate))]
|
||||||
|
pub struct DataCreateServerChannel {
|
||||||
|
/// Channel type
|
||||||
|
#[serde(rename = "type", default = "LegacyServerChannelType::default")]
|
||||||
|
pub channel_type: LegacyServerChannelType,
|
||||||
|
/// Channel name
|
||||||
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))]
|
||||||
|
pub name: String,
|
||||||
|
/// Channel description
|
||||||
|
#[cfg_attr(feature = "validator", validate(length(min = 0, max = 1024)))]
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// Whether this channel is age restricted
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub nsfw: Option<bool>,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
impl Channel {
|
impl Channel {
|
||||||
|
|||||||
@@ -1,119 +1,39 @@
|
|||||||
use std::collections::HashMap;
|
use revolt_database::util::permissions::DatabasePermissionQuery;
|
||||||
|
use revolt_database::{util::reference::Reference, Channel, Database, User};
|
||||||
use revolt_quark::{
|
use revolt_models::v0;
|
||||||
models::{server::PartialServer, Channel, User},
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
perms, Db, Error, Permission, Ref, Result, variables::delta::MAX_CHANNEL_COUNT,
|
use revolt_result::{create_error, Result};
|
||||||
};
|
|
||||||
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use serde::{Deserialize, Serialize};
|
use rocket::State;
|
||||||
use ulid::Ulid;
|
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
/// # Channel Type
|
|
||||||
#[derive(Serialize, Deserialize, JsonSchema, Default)]
|
|
||||||
enum ChannelType {
|
|
||||||
/// Text Channel
|
|
||||||
#[default]
|
|
||||||
Text,
|
|
||||||
/// Voice Channel
|
|
||||||
Voice,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Channel Data
|
|
||||||
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
|
|
||||||
pub struct DataCreateChannel {
|
|
||||||
/// Channel type
|
|
||||||
#[serde(rename = "type", default = "ChannelType::default")]
|
|
||||||
channel_type: ChannelType,
|
|
||||||
/// Channel name
|
|
||||||
#[validate(length(min = 1, max = 32))]
|
|
||||||
name: String,
|
|
||||||
/// Channel description
|
|
||||||
#[validate(length(min = 0, max = 1024))]
|
|
||||||
description: Option<String>,
|
|
||||||
/// Whether this channel is age restricted
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
nsfw: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Create Channel
|
/// # Create Channel
|
||||||
///
|
///
|
||||||
/// Create a new Text or Voice channel.
|
/// Create a new Text or Voice channel.
|
||||||
#[openapi(tag = "Server Information")]
|
#[openapi(tag = "Server Information")]
|
||||||
#[post("/<target>/channels", data = "<info>")]
|
#[post("/<server>/channels", data = "<data>")]
|
||||||
pub async fn req(
|
pub async fn create_server_channel(
|
||||||
db: &Db,
|
db: &State<Database>,
|
||||||
user: User,
|
user: User,
|
||||||
target: Ref,
|
server: Reference,
|
||||||
info: Json<DataCreateChannel>,
|
data: Json<v0::DataCreateServerChannel>,
|
||||||
) -> Result<Json<Channel>> {
|
) -> Result<Json<v0::Channel>> {
|
||||||
let info = info.into_inner();
|
let data = data.into_inner();
|
||||||
info.validate()
|
data.validate().map_err(|error| {
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
create_error!(FailedValidation {
|
||||||
|
error: error.to_string()
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut server = target.as_server(db).await?;
|
let mut server = server.as_server(db).await?;
|
||||||
perms(&user)
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||||
.server(&server)
|
calculate_server_permissions(&mut query)
|
||||||
.throw_permission(db, Permission::ManageChannel)
|
.await
|
||||||
.await?;
|
.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?;
|
||||||
|
|
||||||
if server.channels.len() > *MAX_CHANNEL_COUNT {
|
Channel::create_server_channel(db, &mut server, data, true)
|
||||||
return Err(Error::TooManyChannels { max: *MAX_CHANNEL_COUNT })
|
.await
|
||||||
};
|
.map(|channel| channel.into())
|
||||||
|
.map(Json)
|
||||||
let id = Ulid::new().to_string();
|
|
||||||
let mut channels = server.channels.clone();
|
|
||||||
channels.push(id.clone());
|
|
||||||
|
|
||||||
let DataCreateChannel {
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
nsfw,
|
|
||||||
channel_type,
|
|
||||||
} = info;
|
|
||||||
let channel = match channel_type {
|
|
||||||
ChannelType::Text => Channel::TextChannel {
|
|
||||||
id,
|
|
||||||
server: server.id.clone(),
|
|
||||||
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
|
|
||||||
icon: None,
|
|
||||||
last_message_id: None,
|
|
||||||
|
|
||||||
default_permissions: None,
|
|
||||||
role_permissions: HashMap::new(),
|
|
||||||
|
|
||||||
nsfw: nsfw.unwrap_or(false),
|
|
||||||
},
|
|
||||||
ChannelType::Voice => Channel::VoiceChannel {
|
|
||||||
id,
|
|
||||||
server: server.id.clone(),
|
|
||||||
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
icon: None,
|
|
||||||
|
|
||||||
default_permissions: None,
|
|
||||||
role_permissions: HashMap::new(),
|
|
||||||
|
|
||||||
nsfw: nsfw.unwrap_or(false),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
channel.create(db).await?;
|
|
||||||
server
|
|
||||||
.update(
|
|
||||||
db,
|
|
||||||
PartialServer {
|
|
||||||
channels: Some(channels),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Json(channel))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
|||||||
server_fetch::req,
|
server_fetch::req,
|
||||||
server_edit::req,
|
server_edit::req,
|
||||||
server_ack::req,
|
server_ack::req,
|
||||||
channel_create::req,
|
channel_create::create_server_channel,
|
||||||
member_fetch_all::req,
|
member_fetch_all::req,
|
||||||
member_remove::req,
|
member_remove::req,
|
||||||
member_fetch::req,
|
member_fetch::req,
|
||||||
|
|||||||
Reference in New Issue
Block a user