Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(model,http,http-ratelimiting): Add support for application role connections metadata get/set operations #2004

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 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 application role connections metadata.
ApplicationRoleConnectionsMetadata(u64),
/// Operating on a channel.
ChannelsId(u64),
/// Operating on a channel's followers.
Expand Down Expand Up @@ -334,6 +336,9 @@ impl FromStr for Path {
| ["applications", id, "guilds", _, "commands", _, "permissions"] => {
ApplicationGuildCommandId(parse_id(id)?)
}
["applications", id, "role-connections", "metadata"] => {
ApplicationRoleConnectionsMetadata(parse_id(id)?)
}
["channels", id] => ChannelsId(parse_id(id)?),
["channels", id, "followers"] => ChannelsIdFollowers(parse_id(id)?),
["channels", id, "invites"] => ChannelsIdInvites(parse_id(id)?),
Expand Down
10 changes: 10 additions & 0 deletions twilight-http/src/client/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod builder;
mod connector;
mod interaction;
mod role_connections;

use self::role_connections::RoleConnectionsClient;
pub use self::{builder::ClientBuilder, interaction::InteractionClient};

#[allow(deprecated)]
Expand Down Expand Up @@ -276,6 +278,14 @@ impl Client {
InteractionClient::new(self, application_id)
}

/// Create an interface for using application role connections.
pub const fn role_connections(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unacceptable (not your fault), it's way to contrived. We need a RFC for how to handle routes requiring an application_id.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine! I'll use this for now in my code as I need something that works immediately. I'll be looking forward to the RFC and the proper implementation of this feature but it is unlikely I'll be taking part in the RFC process itself in the near future.

&self,
application_id: Id<ApplicationMarker>,
) -> RoleConnectionsClient<'_> {
RoleConnectionsClient::new(self, application_id)
}

/// Get an immutable reference to the default [`AllowedMentions`] for sent
/// messages.
pub const fn default_allowed_mentions(&self) -> Option<&AllowedMentions> {
Expand Down
44 changes: 44 additions & 0 deletions twilight-http/src/client/role_connections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use crate::{
request::application::role_connections::{GetMetadata, SetMetadata},
Client,
};
use twilight_model::{
application::role_connection::Metadata,
id::{marker::ApplicationMarker, Id},
};

/// Client interface for application role connections.
#[derive(Debug)]
pub struct RoleConnectionsClient<'a> {
application_id: Id<ApplicationMarker>,
client: &'a Client,
}

impl<'a> RoleConnectionsClient<'a> {
/// Create a new interface for using interactions.
pub(super) const fn new(client: &'a Client, application_id: Id<ApplicationMarker>) -> Self {
Self {
application_id,
client,
}
}

/// Get application role connections metadata.
pub const fn metadata(&'a self) -> GetMetadata<'a> {
GetMetadata::new(self.client, self.application_id)
}

/// Set the application role connections metadata.
pub const fn set_metadata(&'a self, records: &'a [Metadata]) -> SetMetadata<'a> {
SetMetadata::new(self.client, self.application_id, records)
}
}

#[cfg(test)]
mod tests {
use super::RoleConnectionsClient;
use static_assertions::assert_impl_all;
use std::fmt::Debug;

assert_impl_all!(RoleConnectionsClient<'_>: Debug, Send, Sync);
}
1 change: 1 addition & 0 deletions twilight-http/src/request/application/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod command;
pub mod interaction;
pub mod role_connections;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As with in twligiht-model, I'd rather have this in guild.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you're referring to. Just to be clear - this is the app-global part of the API, not the user-specific (or guild-specific) one.

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use crate::{
client::Client,
error::Error,
request::{Request, TryIntoRequest},
response::{marker::ListBody, Response, ResponseFuture},
routing::Route,
};
use std::future::IntoFuture;
use twilight_model::{
application::role_connection::Metadata,
id::{marker::ApplicationMarker, Id},
};

/// Get Application Role Connection Metadata Records.
#[must_use = "requests must be configured and executed"]
pub struct GetMetadata<'a> {
application_id: Id<ApplicationMarker>,
http: &'a Client,
}

impl<'a> GetMetadata<'a> {
pub(crate) const fn new(http: &'a Client, application_id: Id<ApplicationMarker>) -> Self {
Self {
application_id,
http,
}
}

/// Execute the request, returning a future resolving to a [`Response`].
#[deprecated(since = "0.14.0", note = "use `.await` or `into_future` instead")]
pub fn exec(self) -> ResponseFuture<ListBody<Metadata>> {
self.into_future()
}
}

impl IntoFuture for GetMetadata<'_> {
type Output = Result<Response<ListBody<Metadata>>, Error>;

type IntoFuture = ResponseFuture<ListBody<Metadata>>;

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 GetMetadata<'_> {
fn try_into_request(self) -> Result<Request, Error> {
Ok(
Request::builder(&Route::GetApplicationRoleConnectionMetadataRecords {
application_id: self.application_id.get(),
})
.build(),
)
}
}
4 changes: 4 additions & 0 deletions twilight-http/src/request/application/role_connections/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod get_metadata;
mod set_metadata;

pub use self::{get_metadata::GetMetadata, set_metadata::SetMetadata};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming could be improved, GetRoleConnection and UpdateRoleConnection sounds a bit better and more futureproof to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True! However, these ones are for configuring and reading the metadata for the role connections that app provides. There also has be an API for setting the actual values of the role connections per user (the @me API).

Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::{
client::Client,
error::Error,
request::{Request, RequestBuilder, TryIntoRequest},
response::{marker::ListBody, Response, ResponseFuture},
routing::Route,
};
use std::future::IntoFuture;
use twilight_model::{
application::role_connection::Metadata,
id::{marker::ApplicationMarker, Id},
};

/// Set a user's linked roles metadata for the given application.
#[must_use = "requests must be configured and executed"]
pub struct SetMetadata<'a> {
application_id: Id<ApplicationMarker>,
http: &'a Client,
records: &'a [Metadata],
}

impl<'a> SetMetadata<'a> {
pub(crate) const fn new(
http: &'a Client,
application_id: Id<ApplicationMarker>,
records: &'a [Metadata],
) -> Self {
Self {
application_id,
http,
records,
}
}

/// Execute the request, returning a future resolving to a [`Response`].
#[deprecated(since = "0.14.0", note = "use `.await` or `into_future` instead")]
pub fn exec(self) -> ResponseFuture<ListBody<Metadata>> {
self.into_future()
}
}

impl IntoFuture for SetMetadata<'_> {
type Output = Result<Response<ListBody<Metadata>>, Error>;

type IntoFuture = ResponseFuture<ListBody<Metadata>>;

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 SetMetadata<'_> {
fn try_into_request(self) -> Result<Request, Error> {
Request::builder(&Route::SetApplicationRoleConnectionMetadataRecords {
application_id: self.application_id.get(),
})
.json(&self.records)
.map(RequestBuilder::build)
}
}
3 changes: 3 additions & 0 deletions twilight-http/src/request/try_into_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod private {
CreateFollowup, CreateResponse, DeleteFollowup, DeleteResponse, GetFollowup,
GetResponse, UpdateFollowup, UpdateResponse,
},
role_connections::{GetMetadata, SetMetadata},
},
channel::{
invite::{CreateInvite, DeleteInvite, GetChannelInvites, GetInvite},
Expand Down Expand Up @@ -206,6 +207,7 @@ mod private {
impl Sealed for GetJoinedPrivateArchivedThreads<'_> {}
impl Sealed for GetMember<'_> {}
impl Sealed for GetMessage<'_> {}
impl Sealed for GetMetadata<'_> {}
impl Sealed for GetNitroStickerPacks<'_> {}
impl Sealed for GetPins<'_> {}
impl Sealed for GetPrivateArchivedThreads<'_> {}
Expand All @@ -232,6 +234,7 @@ mod private {
impl Sealed for SearchGuildMembers<'_> {}
impl Sealed for SetGlobalCommands<'_> {}
impl Sealed for SetGuildCommands<'_> {}
impl Sealed for SetMetadata<'_> {}
impl Sealed for SyncTemplate<'_> {}
impl Sealed for UpdateAutoModerationRule<'_> {}
impl Sealed for UpdateChannel<'_> {}
Expand Down
24 changes: 24 additions & 0 deletions twilight-http/src/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,12 @@ pub enum Route<'a> {
/// ID of the guild.
guild_id: u64,
},
/// Returns a list of application role connection metadata objects for the
/// given application.
GetApplicationRoleConnectionMetadataRecords {
/// The ID of the owner application.
application_id: u64,
},
/// Route information to get a paginated list of audit logs in a guild.
GetAuditLogs {
/// The type of action to get audit logs for.
Expand Down Expand Up @@ -861,6 +867,12 @@ pub enum Route<'a> {
/// Query to search by.
query: &'a str,
},
/// Updates and returns a list of application role connection metadata
/// objects for the given application.
SetApplicationRoleConnectionMetadataRecords {
/// The ID of the owner application.
application_id: u64,
},
/// Route information to set global commands.
SetGlobalCommands {
/// The ID of the owner application.
Expand Down Expand Up @@ -1130,6 +1142,7 @@ impl<'a> Route<'a> {
| Self::RemoveThreadMember { .. }
| Self::UnpinMessage { .. } => Method::Delete,
Self::GetActiveThreads { .. }
| Self::GetApplicationRoleConnectionMetadataRecords { .. }
| Self::GetAuditLogs { .. }
| Self::GetAutoModerationRule { .. }
| Self::GetBan { .. }
Expand Down Expand Up @@ -1259,6 +1272,7 @@ impl<'a> Route<'a> {
| Self::CreateReaction { .. }
| Self::JoinThread { .. }
| Self::PinMessage { .. }
| Self::SetApplicationRoleConnectionMetadataRecords { .. }
| Self::SetGlobalCommands { .. }
| Self::SetGuildCommands { .. }
| Self::SyncTemplate { .. }
Expand Down Expand Up @@ -1329,6 +1343,10 @@ impl<'a> Route<'a> {
| Self::SetGlobalCommands { application_id } => {
Path::ApplicationCommand(application_id)
}
Self::GetApplicationRoleConnectionMetadataRecords { application_id }
| Self::SetApplicationRoleConnectionMetadataRecords { application_id } => {
Path::ApplicationRoleConnectionsMetadata(application_id)
}
Self::CreateGuild => Path::Guilds,
Self::CreateGuildFromTemplate { template_code, .. }
| Self::GetTemplate { template_code, .. } => {
Expand Down Expand Up @@ -1743,6 +1761,12 @@ impl Display for Route<'_> {

Ok(())
}
Route::GetApplicationRoleConnectionMetadataRecords { application_id }
| Route::SetApplicationRoleConnectionMetadataRecords { application_id } => {
f.write_str("applications/")?;
Display::fmt(application_id, f)?;
f.write_str("/role-connections/metadata")
}
Route::CreateGuild => f.write_str("guilds"),
Route::CreateGuildCommand {
application_id,
Expand Down
1 change: 1 addition & 0 deletions twilight-model/src/application/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod command;
pub mod interaction;
pub mod role_connection;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather place this into guild.

54 changes: 54 additions & 0 deletions twilight-model/src/application/role_connection/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! Application role connections models.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer a more descriptive module description. Something summarizing what and why role connections exists would be ideal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is largely a wip at the moment, the docs are either placeholders or taken straight out of the discord's docs (the uncapitalized ones).
When it is deemed this PR makes sense I'll put more effort into correcting the docs, but for now, it's a bit premature.


use std::collections::HashMap;

zeylahellyer marked this conversation as resolved.
Show resolved Hide resolved
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};

/// Application Role Connection Metadata Type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize_repr, Serialize_repr)]
#[repr(u8)]
pub enum MetadataType {
/// the metadata value (integer) is less than or equal to the guild's
zeylahellyer marked this conversation as resolved.
Show resolved Hide resolved
/// configured value (integer)
IntegerLessThanOrEqual = 1,
/// the metadata value (integer) is greater than or equal to the guild's
/// configured value (integer)
IntegerGreaterThanOrEqual = 2,
/// the metadata value (integer) is equal to the guild's configured value
/// (integer)
IntegerEqual = 3,
/// the metadata value (integer) is not equal to the guild's configured
/// value (integer)
IntegerNotEqual = 4,
/// the metadata value (ISO8601 string) is less than or equal to
/// the guild's configured value (integer; days before current date)
DatetimeLessThanOrEqual = 5,
/// the metadata value (ISO8601 string) is greater than or equal to
/// the guild's configured value (integer; days before current date)
DatetimeGreaterThanOrEqual = 6,
/// the metadata value (integer) is equal to the guild's configured value
/// (integer; 1)
BooleanEqual = 7,
/// the metadata value (integer) is not equal to the guild's configured
/// value (integer; 1)
BooleanNotEqual = 8,
}

/// Application Role Connection Metadata Structure.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Metadata {
/// type of metadata value
pub r#type: MetadataType,
zeylahellyer marked this conversation as resolved.
Show resolved Hide resolved
/// dictionary key for the metadata field
/// (must be a-z, 0-9, or _ characters; max 50 characters)
pub key: String,
/// name of the metadata field (max 100 characters)
pub name: String,
/// translations of the name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you update the localization items' documentation to follow the same style as CommandOption?

pub name_localizations: HashMap<String, String>,
/// description of the metadata field (max 200 characters)
pub description: String,
/// translations of the description
zeylahellyer marked this conversation as resolved.
Show resolved Hide resolved
pub description_localizations: HashMap<String, String>,
}