feat: move all json and validate to new utils

This commit is contained in:
Zomatree
2025-07-01 21:42:45 +01:00
parent 3d6f39a0eb
commit cf4fe859bf
83 changed files with 371 additions and 451 deletions

View File

@@ -26,3 +26,7 @@ disallowed-methods = [
# Prefer to use Object::delete(&self)
"revolt_database::models::bots::ops::AbstractBots::delete_bot",
]
disallowed-types = [
"rocket::serde::json::Json",
]

View File

@@ -10,7 +10,6 @@ use revolt_models::v0::{
use revolt_permissions::{calculate_channel_permissions, ChannelPermission, PermissionValue};
use revolt_result::{ErrorType, Result};
use ulid::Ulid;
use validator::Validate;
use crate::{
events::client::EventV1,
@@ -717,12 +716,6 @@ impl Message {
/// Create text embed from sendable embed
pub async fn create_embed(&self, db: &Database, embed: SendableEmbed) -> Result<Embed> {
embed.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let media = if let Some(id) = embed.media {
Some(File::use_attachment(db, &id, &self.id, &self.author).await?)
} else {

View File

@@ -7,6 +7,7 @@ mod embeds;
mod emojis;
mod files;
mod messages;
mod onboard;
mod policy_changes;
mod safety_reports;
mod server_bans;
@@ -24,6 +25,7 @@ pub use embeds::*;
pub use emojis::*;
pub use files::*;
pub use messages::*;
pub use onboard::*;
pub use policy_changes::*;
pub use safety_reports::*;
pub use server_bans::*;

View File

@@ -0,0 +1,15 @@
auto_derived!(
/// # New User Data
#[derive(validator::Validate)]
pub struct DataOnboard {
/// New username which will be used to identify the user on the platform
#[validate(length(min = 2, max = 32), regex = "super::RE_USERNAME")]
pub username: String,
}
/// # Onboarding Status
pub struct DataHello {
/// Whether onboarding is required
pub onboarding: bool,
}
);

View File

@@ -1,6 +1,17 @@
use iso8601_timestamp::Timestamp;
auto_derived!(
/// # Report Data
#[derive(validator::Validate)]
pub struct DataReportContent {
/// Content being reported
pub content: ReportedContent,
/// Additional report description
#[validate(length(min = 0, max = 1000))]
#[serde(default)]
pub additional_context: String,
}
/// User-generated platform moderation report
pub struct Report {
/// Unique Id

View File

@@ -1,4 +1,4 @@
use super::{Channel, File, RE_COLOUR};
use super::{Channel, File, Member, User, RE_COLOUR};
use revolt_permissions::{Override, OverrideField};
use std::collections::HashMap;
@@ -293,4 +293,22 @@ auto_derived!(
pub struct DataEditRoleRanks {
pub ranks: Vec<String>,
}
/// # Query Parameters
#[derive(FromForm)]
pub struct OptionsQueryMembers {
/// String to search for
pub query: String,
/// Discourage use of this API
pub experimental_api: bool,
}
/// # Query members by name
pub struct MemberQueryResponse {
/// List of members
pub members: Vec<Member>,
/// List of users
pub users: Vec<User>,
}
);

View File

@@ -275,6 +275,17 @@ auto_derived!(
/// Username and discriminator combo separated by #
pub username: String,
}
/// # Username Information
#[derive(Validate)]
pub struct DataChangeUsername {
/// New username
#[validate(length(min = 2, max = 32), regex = "super::RE_USERNAME")]
pub username: String,
/// Current account password
#[validate(length(min = 8, max = 1024))]
pub password: String,
}
);
pub trait CheckRelationship {

View File

@@ -1,9 +1,8 @@
use crate::util::json::{Json, Validate};
use revolt_database::{Bot, Database, User};
use revolt_models::v0;
use revolt_result::{create_error, Result};
use rocket::serde::json::Json;
use revolt_result::Result;
use rocket::State;
use validator::Validate;
/// # Create Bot
///
@@ -13,14 +12,9 @@ use validator::Validate;
pub async fn create_bot(
db: &State<Database>,
user: User,
info: Json<v0::DataCreateBot>,
info: Validate<Json<v0::DataCreateBot>>,
) -> Result<Json<v0::BotWithUserResponse>> {
let info = info.into_inner();
info.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let info = info.into_inner().into_inner();
let (bot, user) = Bot::create(db, info.name, &user, None).await?;
Ok(Json(v0::BotWithUserResponse {

View File

@@ -3,8 +3,7 @@ use revolt_models::v0::{self, DataEditBot};
use revolt_result::{create_error, Result};
use rocket::State;
use rocket::serde::json::Json;
use validator::Validate;
use crate::util::json::{Json, Validate};
/// # Edit Bot
///
@@ -15,14 +14,9 @@ pub async fn edit_bot(
db: &State<Database>,
user: User,
target: Reference<'_>,
data: Json<DataEditBot>,
data: Validate<Json<DataEditBot>>,
) -> Result<Json<v0::BotWithUserResponse>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let mut bot = target.as_bot(db).await?;
if bot.owner != user.id {

View File

@@ -1,7 +1,8 @@
use revolt_database::{util::reference::Reference, Database, User};
use revolt_models::v0::FetchBotResponse;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Bot
///

View File

@@ -2,7 +2,7 @@ use futures::future::join_all;
use revolt_database::{Database, User};
use revolt_models::v0::OwnedBotsResponse;
use revolt_result::Result;
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Fetch Owned Bots

View File

@@ -2,7 +2,7 @@ use revolt_database::{util::reference::Reference, Database, User};
use revolt_models::v0::PublicBot;
use revolt_result::{create_error, Result};
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Fetch Public Bot

View File

@@ -8,7 +8,7 @@ use revolt_permissions::{
use revolt_result::{create_error, Result};
use rocket::State;
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket_empty::EmptyResponse;
/// # Invite Bot

View File

@@ -5,8 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use validator::Validate;
use rocket::State;
use crate::util::json::{Json, Validate};
/// # Edit Channel
///
@@ -18,14 +18,9 @@ pub async fn edit(
amqp: &State<AMQP>,
user: User,
target: Reference<'_>,
data: Json<v0::DataEditChannel>,
data: Validate<Json<v0::DataEditChannel>>,
) -> Result<Json<v0::Channel>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let mut channel = target.as_channel(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).channel(&channel);

View File

@@ -6,7 +6,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Channel
///

View File

@@ -2,9 +2,8 @@ use revolt_database::{Channel, Database, RelationshipStatus, User};
use revolt_models::v0;
use revolt_result::{create_error, Result};
use rocket::serde::json::Json;
use crate::util::json::{Json, Validate};
use rocket::State;
use validator::Validate;
/// # Create Group
///
@@ -14,18 +13,13 @@ use validator::Validate;
pub async fn create_group(
db: &State<Database>,
user: User,
data: Json<v0::DataCreateGroup>,
data: Validate<Json<v0::DataCreateGroup>>,
) -> Result<Json<v0::Channel>> {
if user.bot.is_some() {
return Err(create_error!(IsBot));
}
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
for target in &data.users {
match user.relationship_with(target) {

View File

@@ -6,7 +6,8 @@ use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Create Invite
///

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Group Members
///

View File

@@ -6,9 +6,9 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use rocket_empty::EmptyResponse;
use validator::Validate;
use crate::util::json::{Json, Validate};
/// # Bulk Delete Messages
///
@@ -23,14 +23,9 @@ pub async fn bulk_delete_messages(
db: &State<Database>,
user: User,
target: Reference<'_>,
options: Json<v0::OptionsBulkDelete>,
options: Validate<Json<v0::OptionsBulkDelete>>,
) -> Result<EmptyResponse> {
let options = options.into_inner();
options.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let options = options.into_inner().into_inner();
for id in &options.ids {
if ulid::Ulid::from_string(id)

View File

@@ -7,8 +7,8 @@ use revolt_database::{
use revolt_models::v0::{self, Embed};
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use validator::Validate;
use rocket::State;
use crate::util::json::{Json, Validate};
/// # Edit Message
///
@@ -20,14 +20,9 @@ pub async fn edit(
user: User,
target: Reference<'_>,
msg: Reference<'_>,
edit: Json<v0::DataEditMessage>,
edit: Validate<Json<v0::DataEditMessage>>,
) -> Result<Json<v0::Message>> {
let edit = edit.into_inner();
edit.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let edit = edit.into_inner().into_inner();
Message::validate_sum(
&edit.content,

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Message
///

View File

@@ -5,8 +5,8 @@ use revolt_database::{
use revolt_models::v0::{self, MessageSort};
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use validator::Validate;
use rocket::State;
use crate::util::json::{Json, Validate};
/// # Fetch Messages
///
@@ -17,14 +17,8 @@ pub async fn query(
db: &State<Database>,
user: User,
target: Reference<'_>,
options: v0::OptionsQueryMessages,
options: Validate<v0::OptionsQueryMessages>,
) -> Result<Json<v0::BulkMessageResponse>> {
options.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
if let Some(MessageSort::Relevance) = options.sort {
return Err(create_error!(InvalidOperation));
}
@@ -43,7 +37,7 @@ pub async fn query(
sort,
nearby,
include_users,
} = options;
} = options.into_inner();
Message::fetch_with_users(
db,

View File

@@ -5,8 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use validator::Validate;
use rocket::State;
use crate::util::json::{Json, Validate};
/// # Search for Messages
///
@@ -17,18 +17,13 @@ pub async fn search(
db: &State<Database>,
user: User,
target: Reference<'_>,
options: Json<v0::DataMessageSearch>,
options: Validate<Json<v0::DataMessageSearch>>,
) -> Result<Json<v0::BulkMessageResponse>> {
if user.bot.is_some() {
return Err(create_error!(IsBot));
}
let options = options.into_inner();
options.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let options = options.into_inner().into_inner();
if options.query.is_some() && options.pinned.is_some() {
return Err(create_error!(InvalidOperation))

View File

@@ -7,10 +7,9 @@ use revolt_database::{Interactions, Message, AMQP};
use revolt_models::v0;
use revolt_permissions::PermissionQuery;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use revolt_result::Result;
use crate::util::json::{Json, Validate};
use rocket::State;
// use validator::Validate;
/// # Send Message
///
@@ -25,7 +24,7 @@ pub async fn message_send(
data: Validate<Json<v0::DataMessageSend>>,
idempotency: IdempotencyKey,
) -> Result<Json<v0::Message>> {
let data = data.into_inner();
let data = data.into_inner().into_inner();
// Ensure we have permissions to send a message
let channel = target.as_channel(db).await?;

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission, Override};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Set Role Permission
///

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0::{self, DataDefaultChannelPermissions};
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Set Default Permission
///

View File

@@ -6,7 +6,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Join Call
///

View File

@@ -7,9 +7,9 @@ use revolt_permissions::{
calculate_channel_permissions, ChannelPermission, DEFAULT_WEBHOOK_PERMISSIONS,
};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::{Json, Validate};
use ulid::Ulid;
use validator::Validate;
/// # Creates a webhook
///
@@ -20,14 +20,9 @@ pub async fn create_webhook(
db: &State<Database>,
user: User,
target: Reference<'_>,
data: Json<v0::CreateWebhookBody>,
data: Validate<Json<v0::CreateWebhookBody>>,
) -> Result<Json<v0::Webhook>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let channel = target.as_channel(db).await?;

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0::Webhook;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Gets all webhooks
///

View File

@@ -3,9 +3,8 @@ use revolt_database::{util::permissions::DatabasePermissionQuery, Database, Emoj
use revolt_models::v0;
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use validator::Validate;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::{Json, Validate};
/// # Create New Emoji
///
@@ -16,16 +15,11 @@ pub async fn create_emoji(
db: &State<Database>,
user: User,
id: String,
data: Json<v0::DataCreateEmoji>,
data: Validate<Json<v0::DataCreateEmoji>>,
) -> Result<Json<v0::Emoji>> {
let config = config().await;
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
// Validate we have permission to write into parent
match &data.parent {

View File

@@ -2,7 +2,8 @@ use revolt_database::{util::reference::Reference, Database};
use revolt_models::v0;
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Emoji
///

View File

@@ -1,7 +1,8 @@
use revolt_database::{util::reference::Reference, Channel, Database, Invite};
use revolt_models::v0;
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Invite
///

View File

@@ -1,7 +1,8 @@
use revolt_database::{util::reference::Reference, Channel, Database, Invite, Member, User, AMQP};
use revolt_models::v0::{self, InviteJoinResponse};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Join Invite
///

View File

@@ -1,27 +1,11 @@
use authifier::models::Session;
use once_cell::sync::Lazy;
use regex::Regex;
use revolt_database::{Database, User};
use revolt_models::v0;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use serde::{Deserialize, Serialize};
use validator::Validate;
use rocket::State;
use crate::util::json::{Json, Validate};
/// Regex for valid usernames
///
/// Block zero width space
/// Block lookalike characters
pub static RE_USERNAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\p{L}|[\d_.-])+$").unwrap());
/// # New User Data
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
pub struct DataOnboard {
/// New username which will be used to identify the user on the platform
#[validate(length(min = 2, max = 32), regex = "RE_USERNAME")]
username: String,
}
/// # Complete Onboarding
///
@@ -32,18 +16,13 @@ pub async fn complete(
db: &State<Database>,
session: Session,
user: Option<User>,
data: Json<DataOnboard>,
data: Validate<Json<v0::DataOnboard>>,
) -> Result<Json<v0::User>> {
if user.is_some() {
return Err(create_error!(AlreadyOnboarded));
}
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
Ok(Json(
User::create(db, data.username, session.user_id, None)

View File

@@ -1,23 +1,16 @@
use authifier::models::Session;
use revolt_database::User;
use revolt_models::v0;
use rocket::serde::json::Json;
use serde::Serialize;
/// # Onboarding Status
#[derive(Serialize, JsonSchema)]
pub struct DataHello {
/// Whether onboarding is required
onboarding: bool,
}
use crate::util::json::Json;
/// # Check Onboarding Status
///
/// This will tell you whether the current account requires onboarding or whether you can continue to send requests as usual. You may skip calling this if you're restoring an existing session.
#[openapi(tag = "Onboarding")]
#[get("/hello")]
pub async fn hello(_session: Session, user: Option<User>) -> Json<DataHello> {
Json(DataHello {
pub async fn hello(_session: Session, user: Option<User>) -> Json<v0::DataHello> {
Json(v0::DataHello {
onboarding: user.is_none(),
})
}

View File

@@ -3,7 +3,8 @@ use authifier::{
Authifier,
};
use revolt_result::{create_database_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
use rocket_empty::EmptyResponse;
/// # Push Subscribe

View File

@@ -1,6 +1,6 @@
use crate::util::json::Json;
use revolt_config::config;
use revolt_result::Result;
use rocket::serde::json::Json;
use serde::Serialize;
/// # hCaptcha Configuration

View File

@@ -1,22 +1,9 @@
use revolt_database::{events::client::EventV1, Database, Report, Snapshot, SnapshotContent, User};
use revolt_models::v0::{ReportStatus, ReportedContent};
use revolt_models::v0::{DataReportContent, ReportStatus, ReportedContent};
use revolt_result::{create_error, Result};
use serde::Deserialize;
use ulid::Ulid;
use validator::Validate;
use rocket::{serde::json::Json, State};
/// # Report Data
#[derive(Validate, Deserialize, JsonSchema)]
pub struct DataReportContent {
/// Content being reported
content: ReportedContent,
/// Additional report description
#[validate(length(min = 0, max = 1000))]
#[serde(default)]
additional_context: String,
}
use rocket::State;
use crate::util::json::{Json, Validate};
/// # Report Content
///
@@ -26,14 +13,9 @@ pub struct DataReportContent {
pub async fn report_content(
db: &State<Database>,
user: User,
data: Json<DataReportContent>,
data: Validate<Json<DataReportContent>>,
) -> Result<()> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
// Bots cannot create reports
if user.bot.is_some() {

View File

@@ -6,8 +6,8 @@ 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;
use rocket::State;
use crate::util::json::{Json, Validate};
/// # Ban User
///
@@ -19,14 +19,9 @@ pub async fn ban(
user: User,
server: Reference<'_>,
target: Reference<'_>,
data: Json<v0::DataBanCreate>,
data: Validate<Json<v0::DataBanCreate>>,
) -> Result<Json<v0::ServerBan>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let server = server.as_server(db).await?;

View File

@@ -6,7 +6,7 @@ use revolt_models::v0;
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
use revolt_result::Result;
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Fetch Bans

View File

@@ -2,11 +2,10 @@ use revolt_database::util::permissions::DatabasePermissionQuery;
use revolt_database::{util::reference::Reference, Channel, Database, User};
use revolt_models::v0;
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use revolt_result::Result;
use rocket::serde::json::Json;
use crate::util::json::{Json, Validate};
use rocket::State;
use validator::Validate;
/// # Create Channel
///
@@ -17,14 +16,9 @@ pub async fn create_server_channel(
db: &State<Database>,
user: User,
server: Reference<'_>,
data: Json<v0::DataCreateServerChannel>,
data: Validate<Json<v0::DataCreateServerChannel>>,
) -> Result<Json<v0::Channel>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let mut server = server.as_server(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::PermissionQuery;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Server Emoji
///

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Invites
///

View File

@@ -8,8 +8,8 @@ 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;
use crate::util::json::{Json, Validate};
use rocket::State;
/// # Edit Member
///
@@ -21,14 +21,9 @@ pub async fn edit(
user: User,
server: Reference<'_>,
member: Reference<'_>,
data: Json<v0::DataMemberEdit>,
data: Validate<Json<v0::DataMemberEdit>>,
) -> Result<Json<v0::Member>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
// Fetch server and member
let mut server = server.as_server(db).await?;

View File

@@ -6,27 +6,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::PermissionQuery;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use serde::{Deserialize, Serialize};
/// # Query Parameters
#[derive(Deserialize, JsonSchema, FromForm)]
pub struct OptionsQueryMembers {
/// String to search for
query: String,
/// Discourage use of this API
experimental_api: bool,
}
/// # Query members by name
#[derive(Serialize, JsonSchema)]
pub struct MemberQueryResponse {
/// List of members
members: Vec<v0::Member>,
/// List of users
users: Vec<v0::User>,
}
use rocket::State;
use crate::util::json::Json;
/// # Query members by name
///
@@ -37,8 +18,8 @@ pub async fn member_experimental_query(
db: &State<Database>,
user: User,
target: Reference<'_>,
options: OptionsQueryMembers,
) -> Result<Json<MemberQueryResponse>> {
options: v0::OptionsQueryMembers,
) -> Result<Json<v0::MemberQueryResponse>> {
if !options.experimental_api {
return Err(create_error!(InternalError));
}
@@ -84,7 +65,7 @@ pub async fn member_experimental_query(
// Take the first ten and return them
let (members, users): (Vec<Member>, Vec<User>) = zipped_vec.into_iter().take(10).unzip();
Ok(Json(MemberQueryResponse {
Ok(Json(v0::MemberQueryResponse {
members: members.into_iter().map(Into::into).collect(),
users: join_all(
users

View File

@@ -1,3 +1,4 @@
use crate::util::json::Json;
use revolt_database::{
util::{permissions::DatabasePermissionQuery, reference::Reference},
Database, User,
@@ -5,7 +6,7 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::PermissionQuery;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
/// # Fetch Member
///

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::PermissionQuery;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Members
///

View File

@@ -1,3 +1,4 @@
use crate::util::json::Json;
use revolt_database::{
util::{permissions::DatabasePermissionQuery, reference::Reference},
Database, User,
@@ -5,7 +6,7 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_server_permissions, ChannelPermission, Override};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
/// # Set Role Permission
///

View File

@@ -7,7 +7,8 @@ use revolt_permissions::{
calculate_server_permissions, ChannelPermission, DataPermissionsValue, Override,
};
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Set Default Permission
///

View File

@@ -6,8 +6,8 @@ use revolt_database::{
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;
use crate::util::json::{Json, Validate};
use rocket::State;
/// # Create Role
///
@@ -18,14 +18,9 @@ pub async fn create(
db: &State<Database>,
user: User,
target: Reference<'_>,
data: Json<v0::DataCreateRole>,
data: Validate<Json<v0::DataCreateRole>>,
) -> Result<Json<v0::NewRoleResponse>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let server = target.as_server(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);

View File

@@ -5,8 +5,8 @@ use revolt_database::{
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;
use crate::util::json::{Json, Validate};
use rocket::State;
/// # Edit Role
///
@@ -18,14 +18,9 @@ pub async fn edit(
user: User,
target: Reference<'_>,
role_id: String,
data: Json<v0::DataEditRole>,
data: Validate<Json<v0::DataEditRole>>,
) -> Result<Json<v0::Role>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let mut server = target.as_server(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Edits server roles ranks
///

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::PermissionQuery;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Role
///

View File

@@ -2,9 +2,8 @@ use revolt_database::{Database, Member, Server, User};
use revolt_models::v0;
use revolt_result::{create_error, Result};
use rocket::serde::json::Json;
use crate::util::json::{Json, Validate};
use rocket::State;
use validator::Validate;
/// # Create Server
///
@@ -14,18 +13,13 @@ use validator::Validate;
pub async fn create_server(
db: &State<Database>,
user: User,
data: Json<v0::DataCreateServer>,
data: Validate<Json<v0::DataCreateServer>>,
) -> Result<Json<v0::CreateServerLegacyResponse>> {
if user.bot.is_some() {
return Err(create_error!(IsBot));
}
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
user.can_acquire_server(db).await?;

View File

@@ -7,8 +7,8 @@ use revolt_database::{
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;
use crate::util::json::{Json, Validate};
use rocket::State;
/// # Edit Server
///
@@ -19,14 +19,9 @@ pub async fn edit(
db: &State<Database>,
user: User,
target: Reference<'_>,
data: Json<v0::DataEditServer>,
data: Validate<Json<v0::DataEditServer>>,
) -> Result<Json<v0::Server>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let mut server = target.as_server(db).await?;
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission, PermissionQuery};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Server
///

View File

@@ -1,7 +1,7 @@
use revolt_database::{Database, User};
use revolt_models::v0;
use revolt_result::Result;
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Fetch Settings

View File

@@ -1,7 +1,7 @@
use revolt_database::{Database, User};
use revolt_models::v0;
use revolt_result::Result;
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Fetch Unreads

View File

@@ -3,7 +3,8 @@ use revolt_models::v0;
use chrono::prelude::*;
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
use rocket_empty::EmptyResponse;
use std::collections::HashMap;

View File

@@ -2,7 +2,7 @@ use revolt_database::util::reference::Reference;
use revolt_database::{Database, User, AMQP};
use revolt_models::v0;
use revolt_result::{create_error, Result};
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Accept Friend Request

View File

@@ -2,7 +2,7 @@ use revolt_database::util::reference::Reference;
use revolt_database::{Database, User};
use revolt_models::v0;
use revolt_result::Result;
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Block User

View File

@@ -1,29 +1,9 @@
use authifier::models::Account;
use once_cell::sync::Lazy;
use regex::Regex;
use revolt_database::{Database, User};
use revolt_models::v0;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use serde::{Deserialize, Serialize};
use validator::Validate;
/// Regex for valid usernames
///
/// Block zero width space
/// Block lookalike characters
pub static RE_USERNAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\p{L}|[\d_.-])+$").unwrap());
/// # Username Information
#[derive(Validate, Serialize, Deserialize, JsonSchema)]
pub struct DataChangeUsername {
/// New username
#[validate(length(min = 2, max = 32), regex = "RE_USERNAME")]
username: String,
/// Current account password
#[validate(length(min = 8, max = 1024))]
password: String,
}
use crate::util::json::{Json, Validate};
use rocket::State;
/// # Change Username
///
@@ -34,14 +14,9 @@ pub async fn change_username(
db: &State<Database>,
account: Account,
mut user: User,
data: Json<DataChangeUsername>,
data: Validate<Json<v0::DataChangeUsername>>,
) -> Result<Json<v0::User>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
account
.verify_password(&data.password)

View File

@@ -2,9 +2,8 @@ use revolt_database::FieldsUser;
use revolt_database::{util::reference::Reference, Database, File, PartialUser, User};
use revolt_models::v0;
use revolt_result::{create_error, Result};
use rocket::serde::json::Json;
use crate::util::json::{Json, Validate};
use rocket::State;
use validator::Validate;
/// # Edit User
///
@@ -15,14 +14,9 @@ pub async fn edit(
db: &State<Database>,
mut user: User,
target: Reference<'_>,
data: Json<v0::DataEditUser>,
data: Validate<Json<v0::DataEditUser>>,
) -> Result<Json<v0::User>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
// Filter out invalid edit fields
if !user.privileged && (data.badges.is_some() || data.flags.is_some()) {

View File

@@ -1,7 +1,8 @@
use revolt_database::{Database, User};
use revolt_models::v0;
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch Direct Message Channels
///

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_user_permissions, UserPermission};
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch User Profile
///

View File

@@ -1,7 +1,7 @@
use revolt_database::User;
use revolt_models::v0;
use revolt_result::Result;
use rocket::serde::json::Json;
use crate::util::json::Json;
/// # Fetch Self
///

View File

@@ -6,7 +6,8 @@ use revolt_models::v0;
use revolt_permissions::{calculate_user_permissions, UserPermission};
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch User
///

View File

@@ -1,7 +1,8 @@
use revolt_database::{util::reference::Reference, Database};
use revolt_models::v0;
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Fetch User Flags
///

View File

@@ -5,7 +5,7 @@ use revolt_models::v0;
use revolt_permissions::{calculate_user_permissions, UserPermission};
use revolt_result::{create_error, Result};
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Fetch Mutual Friends And Servers

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0;
use revolt_permissions::{calculate_user_permissions, UserPermission};
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Open Direct Message
///

View File

@@ -2,7 +2,7 @@ use revolt_database::util::reference::Reference;
use revolt_database::{Database, User};
use revolt_models::v0;
use revolt_result::{create_error, Result};
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Deny Friend Request / Remove Friend

View File

@@ -2,7 +2,7 @@
use revolt_database::{Database, User, AMQP};
use revolt_models::v0;
use revolt_result::{create_error, Result};
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Send Friend Request

View File

@@ -2,7 +2,7 @@ use revolt_database::util::reference::Reference;
use revolt_database::{Database, User};
use revolt_models::v0;
use revolt_result::Result;
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::State;
/// # Unblock User

View File

@@ -4,9 +4,9 @@ use revolt_database::{
};
use revolt_models::v0::{DataEditWebhook, Webhook};
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use validator::Validate;
use revolt_result::Result;
use crate::util::json::{Json, Validate};
use rocket::State;
/// # Edits a webhook
///
@@ -17,14 +17,9 @@ pub async fn webhook_edit(
db: &State<Database>,
webhook_id: Reference<'_>,
user: User,
data: Json<DataEditWebhook>,
data: Validate<Json<DataEditWebhook>>,
) -> Result<Json<Webhook>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let mut webhook = webhook_id.as_webhook(db).await?;
let channel = db.fetch_channel(&webhook.channel_id).await?;

View File

@@ -1,9 +1,9 @@
use revolt_database::util::reference::Reference;
use revolt_database::{Database, File, PartialWebhook};
use revolt_models::v0::{DataEditWebhook, Webhook};
use revolt_models::validator::Validate;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use revolt_result::Result;
use crate::util::json::{Json, Validate};
use rocket::State;
/// # Edits a webhook
///
@@ -14,14 +14,9 @@ pub async fn webhook_edit_token(
db: &State<Database>,
webhook_id: Reference<'_>,
token: String,
data: Json<DataEditWebhook>,
data: Validate<Json<DataEditWebhook>>,
) -> Result<Json<Webhook>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let mut webhook = webhook_id.as_webhook(db).await?;
webhook.assert_token(&token)?;

View File

@@ -5,10 +5,9 @@ use revolt_database::{
};
use revolt_models::v0;
use revolt_permissions::{ChannelPermission, PermissionValue};
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
use validator::Validate;
use revolt_result::Result;
use crate::util::json::{Json, Validate};
use rocket::State;
/// # Executes a webhook
///
@@ -20,15 +19,10 @@ pub async fn webhook_execute(
amqp: &State<AMQP>,
webhook_id: Reference<'_>,
token: String,
data: Json<v0::DataMessageSend>,
data: Validate<Json<v0::DataMessageSend>>,
idempotency: IdempotencyKey,
) -> Result<Json<v0::Message>> {
let data = data.into_inner();
data.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let data = data.into_inner().into_inner();
let webhook = webhook_id.as_webhook(db).await?;
webhook.assert_token(&token)?;

View File

@@ -1,6 +1,6 @@
use revolt_database::{util::reference::Reference, Database, Message, AMQP};
use revolt_models::v0::{MessageAuthor, SendableEmbed, Webhook};
use revolt_result::{create_error, Error, Result};
use revolt_result::{create_error, Error, Result, ToRevoltError};
use revolt_rocket_okapi::{
gen::OpenApiGenerator,
request::{OpenApiFromRequest, RequestHeaderInput},
@@ -1061,11 +1061,13 @@ pub async fn webhook_execute_github(
},
};
sendable_embed.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
sendable_embed.validate()
.capture_error()
.map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
let message_id = Ulid::new().to_string();

View File

@@ -5,7 +5,8 @@ use revolt_database::{
use revolt_models::v0::{ResponseWebhook, Webhook};
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Gets a webhook
///

View File

@@ -1,7 +1,8 @@
use revolt_database::{util::reference::Reference, Database};
use revolt_models::v0::Webhook;
use revolt_result::Result;
use rocket::{serde::json::Json, State};
use rocket::State;
use crate::util::json::Json;
/// # Gets a webhook
///

View File

@@ -2,16 +2,11 @@ use std::fmt::Debug;
use revolt_rocket_okapi::{
r#gen::OpenApiGenerator,
response::OpenApiResponderInner,
revolt_okapi::openapi3::Responses,
util::add_schema_response,
request::OpenApiFromData,
revolt_okapi::{
openapi3::{MediaType, RequestBody},
Map,
},
response::OpenApiResponderInner,
revolt_okapi::openapi3::{RequestBody, Responses}, util::add_schema_response
};
use rocket::data::{Data, FromData, Limits, Outcome};
use rocket::{data::{Data, FromData, Limits, Outcome}};
use rocket::response::{self, Responder, content};
use rocket::request::{local_cache, Request};
use schemars::JsonSchema;
@@ -21,27 +16,6 @@ use revolt_result::{create_error, Error, ToRevoltError};
// A lot of this code is modified versions of rocket::serde::json so we can store
// the error so it can be passed to the error catcher.
macro_rules! fn_request_body {
($gen:ident, $ty:path, $mime_type:expr) => {{
let schema = $gen.json_schema::<$ty>();
Ok(RequestBody {
content: {
let mut map = Map::new();
map.insert(
$mime_type.to_owned(),
MediaType {
schema: Some(schema),
..MediaType::default()
},
);
map
},
required: true,
..revolt_rocket_okapi::revolt_okapi::openapi3::RequestBody::default()
})
}};
}
#[derive(Debug, Clone)]
pub struct Json<T>(pub T);
@@ -107,11 +81,11 @@ impl<'r, T: Serialize> Responder<'r, 'static> for Json<T> {
impl<'r, T: JsonSchema + Deserialize<'r> + Debug> OpenApiFromData<'r> for Json<T> {
fn request_body(gen: &mut OpenApiGenerator) -> revolt_rocket_okapi::Result<RequestBody> {
fn_request_body!(gen, T, "application/json")
crate::fn_request_body!(gen, T, "application/json")
}
}
impl<T: Serialize + JsonSchema + Send> OpenApiResponderInner for Json<T> {
impl<T: JsonSchema + Serialize> OpenApiResponderInner for Json<T> {
fn responses(gen: &mut OpenApiGenerator) -> revolt_rocket_okapi::Result<Responses> {
let mut responses = Responses::default();
let schema = gen.json_schema::<T>();
@@ -125,52 +99,3 @@ impl<T: validator::Validate> validator::Validate for Json<T> {
self.0.validate()
}
}
pub struct Validate<T>(pub T);
impl<T> Validate<Json<T>> {
pub fn into_inner(self) -> T {
self.0.0
}
}
#[async_trait]
impl<'r, T: validator::Validate + Deserialize<'r> + Debug> FromData<'r> for Validate<Json<T>> {
type Error = Error;
async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
<Json::<T> as FromData<'r>>::from_data(req, data).await.and_then(|inner| {
if let Err(e) = inner.validate() {
let error = create_error!(FailedValidation { error: e.to_string() });
req.local_cache(|| Some(error.clone()));
return Outcome::Error((error.rocket_status(), error))
};
Outcome::Success(Self(inner))
})
}
}
impl<'r, T: JsonSchema + Deserialize<'r> + validator::Validate + Debug> OpenApiFromData<'r> for Validate<Json<T>> {
fn request_body(gen: &mut OpenApiGenerator) -> revolt_rocket_okapi::Result<RequestBody> {
fn_request_body!(gen, T, "application/json")
}
}
impl<T: Serialize + JsonSchema + Send> OpenApiResponderInner for Validate<Json<T>> {
fn responses(gen: &mut OpenApiGenerator) -> revolt_rocket_okapi::Result<Responses> {
let mut responses = Responses::default();
let schema = gen.json_schema::<T>();
add_schema_response(&mut responses, 200, "application/json", schema)?;
Ok(responses)
}
}
impl<T> std::ops::Deref for Validate<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View File

@@ -0,0 +1,27 @@
pub mod json;
pub mod validator;
pub use json::Json;
pub use validator::Validate;
#[macro_export]
macro_rules! fn_request_body {
($gen:ident, $ty:path, $mime_type:expr) => {{
let schema = $gen.json_schema::<$ty>();
Ok(revolt_rocket_okapi::revolt_okapi::openapi3::RequestBody {
content: {
let mut map = revolt_rocket_okapi::revolt_okapi::Map::new();
map.insert(
$mime_type.to_owned(),
revolt_rocket_okapi::revolt_okapi::openapi3::MediaType {
schema: Some(schema),
..Default::default()
},
);
map
},
required: true,
..Default::default()
})
}};
}

View File

@@ -0,0 +1,87 @@
use revolt_rocket_okapi::{
r#gen::OpenApiGenerator,
request::OpenApiFromData,
response::OpenApiResponderInner,
revolt_okapi::{openapi3::{RequestBody, Responses}},
};
use rocket::{data::{Data, FromData, Outcome}, form};
use rocket::request::Request;
use schemars::JsonSchema;
use revolt_result::{create_error, Error};
pub struct Validate<T>(pub T);
impl<T> Validate<T> {
pub fn into_inner(self) -> T {
self.0
}
}
#[async_trait]
impl<'r, T: FromData<'r, Error = Error> + validator::Validate> FromData<'r> for Validate<T> {
type Error = Error;
async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
T::from_data(req, data).await.and_then(|inner| {
if let Err(e) = inner.validate() {
let error = create_error!(FailedValidation { error: e.to_string() });
req.local_cache(|| Some(error.clone()));
return Outcome::Error((error.rocket_status(), error))
};
Outcome::Success(Self(inner))
})
}
}
#[async_trait]
impl<'r, T: form::FromForm<'r> + JsonSchema> form::FromForm<'r> for Validate<T> {
type Context = T::Context;
fn init(opts: form::Options) -> Self::Context {
<T as form::FromForm<'r>>::init(opts)
}
fn push_value(ctxt: &mut Self::Context, field: form::ValueField<'r>) {
<T as form::FromForm<'r>>::push_value(ctxt, field);
}
async fn push_data(ctxt: &mut Self::Context, field: form::DataField<'r, '_>) {
<T as form::FromForm<'r>>::push_data(ctxt, field).await;
}
fn finalize(ctxt: Self::Context) -> form::Result<'r, Self> {
<T as form::FromForm<'r>>::finalize(ctxt).map(Self)
}
}
impl<'r, T: FromData<'r, Error = Error> + OpenApiFromData<'r> + validator::Validate> OpenApiFromData<'r> for Validate<T> {
fn request_body(gen: &mut OpenApiGenerator) -> revolt_rocket_okapi::Result<RequestBody> {
T::request_body(gen)
}
}
impl<T: JsonSchema> JsonSchema for Validate<T> {
fn schema_name() -> String {
T::schema_name()
}
fn json_schema(generator: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
T::json_schema(generator)
}
}
impl<T: OpenApiResponderInner> OpenApiResponderInner for Validate<T> {
fn responses(gen: &mut OpenApiGenerator) -> revolt_rocket_okapi::Result<Responses> {
T::responses(gen)
}
}
impl<T> std::ops::Deref for Validate<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View File

@@ -8,7 +8,7 @@ use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::uri::Origin;
use rocket::http::{Method, Status};
use rocket::request::{FromRequest, Outcome};
use rocket::serde::json::Json;
use crate::util::json::Json;
use rocket::{Data, Request, Response};
use revolt_rocket_okapi::gen::OpenApiGenerator;