Skip to content

Commit

Permalink
feat(model, cache): Add support for application editing and new appli…
Browse files Browse the repository at this point in the history
…cation fields (#2284)

Ref:
- discord/discord-api-docs#6297

Additionally, this changes the path for `get_user_application` to be
`/application/@me` instead of `/oauth2/applications/@me` for
consistency. This returns the same data and shouldn't effect any users.
  • Loading branch information
suneettipirneni committed Dec 30, 2023
1 parent e4f700e commit 29d2133
Show file tree
Hide file tree
Showing 9 changed files with 258 additions and 8 deletions.
3 changes: 3 additions & 0 deletions twilight-http-ratelimiting/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ pub enum Path {
ApplicationGuildCommand(u64),
/// Operating on a specific command in a guild.
ApplicationGuildCommandId(u64),
/// Operating on current user application,
ApplicationsMe,
/// Operating on a channel.
ChannelsId(u64),
/// Operating on a channel's followers.
Expand Down Expand Up @@ -330,6 +332,7 @@ impl FromStr for Path {
let parts = s.split('/').skip(skip).collect::<Vec<&str>>();

Ok(match parts[..] {
["applications", "me"] => ApplicationsMe,
["applications", id, "commands"] => ApplicationCommand(parse_id(id)?),
["applications", id, "commands", _] => ApplicationCommandId(parse_id(id)?),
["applications", id, "guilds", _, "commands"]
Expand Down
7 changes: 6 additions & 1 deletion twilight-http/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::request::{
update_guild_onboarding::{UpdateGuildOnboarding, UpdateGuildOnboardingFields},
GetGuildOnboarding,
},
GetCurrentAuthorizationInformation,
GetCurrentAuthorizationInformation, UpdateCurrentUserApplication,
};
#[allow(deprecated)]
use crate::{
Expand Down Expand Up @@ -698,6 +698,11 @@ impl Client {
GetUserApplicationInfo::new(self)
}

/// Update the current user's application.
pub const fn update_current_user_application(&self) -> UpdateCurrentUserApplication<'_> {
UpdateCurrentUserApplication::new(self)
}

/// Update the current user.
///
/// All parameters are optional. If the username is changed, it may cause the discriminator to
Expand Down
2 changes: 2 additions & 0 deletions twilight-http/src/request/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ mod get_user_application;
mod get_voice_regions;
mod multipart;
mod try_into_request;
mod update_user_application;

pub use self::{
audit_reason::AuditLogReason,
Expand All @@ -69,6 +70,7 @@ pub use self::{
get_voice_regions::GetVoiceRegions,
multipart::Form,
try_into_request::TryIntoRequest,
update_user_application::UpdateCurrentUserApplication,
};
pub use twilight_http_ratelimiting::request::Method;

Expand Down
2 changes: 2 additions & 0 deletions twilight-http/src/request/try_into_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ mod private {
CreateGuildFromTemplate, CreateTemplate, DeleteTemplate, GetTemplate, GetTemplates,
SyncTemplate, UpdateTemplate,
},
update_user_application::UpdateCurrentUserApplication,
user::{
CreatePrivateChannel, GetCurrentUser, GetCurrentUserConnections,
GetCurrentUserGuildMember, GetCurrentUserGuilds, GetUser, LeaveGuild,
Expand Down Expand Up @@ -269,6 +270,7 @@ mod private {
impl Sealed for UpdateWebhook<'_> {}
impl Sealed for UpdateWebhookMessage<'_> {}
impl Sealed for UpdateWebhookWithToken<'_> {}
impl Sealed for UpdateCurrentUserApplication<'_> {}
}

use super::base::Request;
Expand Down
180 changes: 180 additions & 0 deletions twilight-http/src/request/update_user_application.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
use std::future::IntoFuture;

use serde::Serialize;
use twilight_model::oauth::{Application, ApplicationFlags, InstallParams};

use crate::{
client::Client,
error::Error,
request::{Nullable, Request, TryIntoRequest},
response::{Response, ResponseFuture},
routing::Route,
};

#[derive(Serialize)]
struct UpdateCurrentUserApplicationFields<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
cover_image: Option<Nullable<&'a str>>,
#[serde(skip_serializing_if = "Option::is_none")]
custom_install_url: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
flags: Option<ApplicationFlags>,
#[serde(skip_serializing_if = "Option::is_none")]
icon: Option<Nullable<&'a str>>,
#[serde(skip_serializing_if = "Option::is_none")]
install_params: Option<InstallParams>,
#[serde(skip_serializing_if = "Option::is_none")]
interactions_endpoint_url: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
role_connections_verification_url: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
tags: Option<Vec<&'a str>>,
}

/// Update the current user's application.
///
/// Returns the newly updated application.
///
/// Refer to [Discord Docs/Update Current User Application][1].
///
/// # Examples
///
/// ```no_run
/// # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use std::env;
/// use twilight_http::Client;
///
/// let bearer_token = env::var("BEARER_TOKEN")?;
///
/// let client = Client::new(bearer_token);
/// let response = client
/// .update_current_user_application()
/// .description("My cool application")
/// .await?;
/// let application = response.model().await?;
///
/// println!("Application: {}", application.description);
///
/// # Ok(()) }
/// ```
///
/// [1]: https://discord.com/developers/docs/resources/application#edit-current-application
#[must_use = "requests must be configured and executed"]
pub struct UpdateCurrentUserApplication<'a> {
fields: UpdateCurrentUserApplicationFields<'a>,
http: &'a Client,
}

impl<'a> UpdateCurrentUserApplication<'a> {
pub(crate) const fn new(http: &'a Client) -> Self {
Self {
fields: UpdateCurrentUserApplicationFields {
cover_image: None,
custom_install_url: None,
description: None,
flags: None,
icon: None,
install_params: None,
interactions_endpoint_url: None,
role_connections_verification_url: None,
tags: None,
},
http,
}
}

/// Sets the cover image of the application.
pub const fn cover_image(mut self, cover_image: Option<&'a str>) -> Self {
self.fields.cover_image = Some(Nullable(cover_image));

self
}

/// Sets the custom install URL of the application.
pub const fn custom_install_url(mut self, custom_install_url: &'a str) -> Self {
self.fields.custom_install_url = Some(custom_install_url);

self
}

/// Sets the description of the application.
pub const fn description(mut self, description: &'a str) -> Self {
self.fields.description = Some(description);

self
}

/// Sets the flags of the application.
/// Only limited intent flags (`GATEWAY_PRESENCE_LIMITED`, `GATEWAY_GUILD_MEMBERS_LIMITED`,
/// and `GATEWAY_MESSAGE_CONTENT_LIMITED`) can be updated via the API.
pub const fn flags(mut self, flags: ApplicationFlags) -> Self {
self.fields.flags = Some(flags);

self
}

/// Sets the icon of the application.
pub const fn icon(mut self, icon: Option<&'a str>) -> Self {
self.fields.icon = Some(Nullable(icon));

self
}

/// Sets the install params of the application.
pub fn install_params(mut self, install_params: InstallParams) -> Self {
self.fields.install_params = Some(install_params);

self
}

/// Sets the interactions endpoint URL of the application.
pub const fn interactions_endpoint_url(mut self, interactions_endpoint_url: &'a str) -> Self {
self.fields.interactions_endpoint_url = Some(interactions_endpoint_url);

self
}

/// Sets the role connections verification URL of the application.
pub const fn role_connections_verification_url(
mut self,
role_connections_verification_url: &'a str,
) -> Self {
self.fields.role_connections_verification_url = Some(role_connections_verification_url);

self
}

/// Sets the tags of the application.
pub fn tags(mut self, tags: Vec<&'a str>) -> Self {
self.fields.tags = Some(tags);

self
}
}

impl IntoFuture for UpdateCurrentUserApplication<'_> {
type Output = Result<Response<Application>, Error>;

type IntoFuture = ResponseFuture<Application>;

fn into_future(self) -> Self::IntoFuture {
let http = self.http;

match self.try_into_request() {
Ok(request) => http.request(request),
Err(source) => ResponseFuture::error(source),
}
}
}

impl TryIntoRequest for UpdateCurrentUserApplication<'_> {
fn try_into_request(self) -> Result<Request, Error> {
let mut request = Request::builder(&Route::UpdateCurrentUserApplication);

request = request.json(&self.fields)?;

Ok(request.build())
}
}
23 changes: 19 additions & 4 deletions twilight-http/src/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use twilight_model::id::{marker::RoleMarker, Id};
#[non_exhaustive]
pub enum Route<'a> {
/// Route information to add a user to a guild.
AddGuildMember { guild_id: u64, user_id: u64 },
AddGuildMember {
guild_id: u64,
user_id: u64,
},
/// Route information to add a role to guild member.
AddMemberRole {
/// The ID of the guild.
Expand Down Expand Up @@ -1099,6 +1102,7 @@ pub enum Route<'a> {
token: &'a str,
webhook_id: u64,
},
UpdateCurrentUserApplication,
}

impl<'a> Route<'a> {
Expand Down Expand Up @@ -1249,6 +1253,7 @@ impl<'a> Route<'a> {
| Self::UpdateTemplate { .. }
| Self::UpdateUserVoiceState { .. }
| Self::UpdateWebhookMessage { .. }
| Self::UpdateCurrentUserApplication
| Self::UpdateWebhook { .. } => Method::Patch,
Self::CreateChannel { .. }
| Self::CreateGlobalCommand { .. }
Expand Down Expand Up @@ -1537,7 +1542,9 @@ impl<'a> Route<'a> {
Path::ApplicationGuildCommandId(application_id)
}
Self::GetCurrentAuthorizationInformation => Path::OauthMe,
Self::GetCurrentUserApplicationInfo => Path::OauthApplicationsMe,
Self::GetCurrentUserApplicationInfo | Self::UpdateCurrentUserApplication => {
Path::ApplicationsMe
}
Self::GetCurrentUser | Self::GetUser { .. } | Self::UpdateCurrentUser => Path::UsersId,
Self::GetCurrentUserGuildMember { .. } => Path::UsersIdGuildsIdMember,
Self::GetEmoji { guild_id, .. } | Self::UpdateEmoji { guild_id, .. } => {
Expand Down Expand Up @@ -2341,7 +2348,9 @@ impl Display for Route<'_> {
f.write_str("/permissions")
}
Route::GetCurrentAuthorizationInformation => f.write_str("oauth2/@me"),
Route::GetCurrentUserApplicationInfo => f.write_str("oauth2/applications/@me"),
Route::GetCurrentUserApplicationInfo | Route::UpdateCurrentUserApplication => {
f.write_str("applications/@me")
}
Route::GetCurrentUser | Route::UpdateCurrentUser => f.write_str("users/@me"),
Route::GetCurrentUserGuildMember { guild_id } => {
f.write_str("users/@me/guilds/")?;
Expand Down Expand Up @@ -4005,7 +4014,13 @@ mod tests {
#[test]
fn get_current_user_application_info() {
let route = Route::GetCurrentUserApplicationInfo;
assert_eq!(route.to_string(), "oauth2/applications/@me");
assert_eq!(route.to_string(), "applications/@me");
}

#[test]
fn update_current_user_application() {
let route = Route::UpdateCurrentUserApplication;
assert_eq!(route.to_string(), "applications/@me");
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion twilight-model/src/guild/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ use serde::{
};
use std::fmt::{Formatter, Result as FmtResult};

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Hash)]
pub struct Guild {
pub afk_channel_id: Option<Id<ChannelMarker>>,
pub afk_timeout: AfkTimeout,
Expand Down
Loading

0 comments on commit 29d2133

Please sign in to comment.