From 8a17d586201eead630509f96703879c2dcfca150 Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Sun, 1 Dec 2024 10:48:51 +0100 Subject: [PATCH 1/4] refactor(helix): move conduits to use twitch_types --- src/eventsub/mod.rs | 12 ++--- src/helix/client/client_ext.rs | 16 +++---- .../endpoints/eventsub/create_conduit.rs | 2 +- .../endpoints/eventsub/get_conduit_shards.rs | 45 ++++++++++++++----- src/helix/endpoints/eventsub/get_conduits.rs | 38 ++-------------- .../eventsub/update_conduit_shards.rs | 41 +++++++++++------ 6 files changed, 79 insertions(+), 75 deletions(-) diff --git a/src/eventsub/mod.rs b/src/eventsub/mod.rs index 1649b736b9..579c717f3b 100644 --- a/src/eventsub/mod.rs +++ b/src/eventsub/mod.rs @@ -976,7 +976,7 @@ pub struct EventSubSubscription { #[cfg_attr(nightly, doc(cfg(feature = "eventsub")))] pub struct Conduit { /// Conduit ID - pub id: String, + pub id: types::ConduitId, /// Number of shards associated with this conduit pub shard_count: usize, } @@ -990,7 +990,7 @@ pub struct Conduit { #[cfg_attr(nightly, doc(cfg(feature = "eventsub")))] pub struct Shard { /// Shard ID. - pub id: String, + pub id: types::ConduitShardId, /// The transport details that you want Twitch to use when sending you notifications. pub transport: Transport, @@ -998,9 +998,9 @@ pub struct Shard { impl Shard { /// Create a shard with a transport set - pub fn new(id: impl std::string::ToString, transport: Transport) -> Self { + pub fn new(id: impl Into, transport: Transport) -> Self { Self { - id: id.to_string(), + id: id.into(), transport, } @@ -1051,7 +1051,7 @@ pub enum ShardStatus { #[non_exhaustive] pub struct ShardError { /// Shard ID. - pub id: String, + pub id: types::ConduitShardId, /// The error that occurred while updating the shard. pub message: String, @@ -1065,7 +1065,7 @@ pub struct ShardError { #[non_exhaustive] pub struct ShardResponse { /// Shard ID. - pub id: String, + pub id: types::ConduitShardId, /// The shard status. The subscriber receives events only for enabled shards. pub status: ShardStatus, diff --git a/src/helix/client/client_ext.rs b/src/helix/client/client_ext.rs index 2f974b9819..7b37857464 100644 --- a/src/helix/client/client_ext.rs +++ b/src/helix/client/client_ext.rs @@ -1713,7 +1713,7 @@ impl<'client, C: crate::HttpClient + Sync + 'client> HelixClient<'client, C> { /// /// # Ok(()) } /// ``` - pub async fn create_conduit<'b: 'client, T>( + pub async fn create_conduit( &'client self, shard_count: usize, token: &'client T, @@ -1781,7 +1781,7 @@ impl<'client, C: crate::HttpClient + Sync + 'client> HelixClient<'client, C> { /// ``` pub fn get_conduit_shards<'b: 'client, T>( &'client self, - conduit_id: impl Into>, + conduit_id: impl types::IntoCow<'b, types::ConduitIdRef> + 'b, status: impl Into>, token: &'client T, ) -> impl futures::Stream>> @@ -1792,7 +1792,7 @@ impl<'client, C: crate::HttpClient + Sync + 'client> HelixClient<'client, C> { T: TwitchToken + Send + Sync + ?Sized, { let req = helix::eventsub::GetConduitShardsRequest { - conduit_id: conduit_id.into(), + conduit_id: conduit_id.into_cow(), status: status.into(), after: None, }; @@ -1833,22 +1833,22 @@ impl<'client, C: crate::HttpClient + Sync + 'client> HelixClient<'client, C> { /// ); /// /// let response = client - /// .update_conduit_shards(conduit_id, vec![shard], &token) + /// .update_conduit_shards(conduit_id, &[shard], &token) /// .await; /// /// # Ok(()) } /// ``` pub async fn update_conduit_shards<'b: 'client, T>( &'client self, - conduit_id: impl Into + Send, - shards: Vec, + conduit_id: impl types::IntoCow<'b, types::ConduitIdRef> + 'b + Send, + shards: impl Into> + Send, token: &'client T, ) -> Result> where T: TwitchToken + Send + Sync + ?Sized, { - let req = helix::eventsub::UpdateConduitShardsRequest {}; - let body = helix::eventsub::UpdateConduitShardsBody::new(conduit_id.into(), shards); + let req = helix::eventsub::UpdateConduitShardsRequest::default(); + let body = helix::eventsub::UpdateConduitShardsBody::new(conduit_id, shards); self.req_patch(req, body, token) .await diff --git a/src/helix/endpoints/eventsub/create_conduit.rs b/src/helix/endpoints/eventsub/create_conduit.rs index c8ae7af83c..152d104266 100644 --- a/src/helix/endpoints/eventsub/create_conduit.rs +++ b/src/helix/endpoints/eventsub/create_conduit.rs @@ -110,7 +110,7 @@ fn test_successful_response() { assert_eq!( response.data, crate::eventsub::Conduit { - id: "bfcfc993-26b1-b876-44d9-afe75a379dac".to_string(), + id: "bfcfc993-26b1-b876-44d9-afe75a379dac".into(), shard_count: 5, }, ); diff --git a/src/helix/endpoints/eventsub/get_conduit_shards.rs b/src/helix/endpoints/eventsub/get_conduit_shards.rs index 3329f098ea..6d1d6bf369 100644 --- a/src/helix/endpoints/eventsub/get_conduit_shards.rs +++ b/src/helix/endpoints/eventsub/get_conduit_shards.rs @@ -8,15 +8,15 @@ use helix::RequestGet; /// Query Parameters for [Get Conduit Shards](super::get_conduit_shards) /// /// [`get-conduit-shards`](https://dev.twitch.tv/docs/api/reference/#get-conduit-shards) -#[derive(PartialEq, Eq, Serialize, Clone, Debug, Default)] +#[derive(PartialEq, Eq, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[must_use] #[non_exhaustive] pub struct GetConduitShardsRequest<'a> { /// Conduit ID. - #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] + #[cfg_attr(feature = "typed-builder", builder(setter(into)))] #[cfg_attr(feature = "deser_borrow", serde(borrow = "'a"))] - pub conduit_id: Cow<'a, str>, + pub conduit_id: Cow<'a, types::ConduitIdRef>, /// Status to filter by. #[cfg_attr(feature = "typed-builder", builder(default, setter(into)))] @@ -28,6 +28,29 @@ pub struct GetConduitShardsRequest<'a> { pub after: Option>, } +impl<'a> GetConduitShardsRequest<'a> { + /// Request the shards of a conduit + pub fn new(conduit_id: impl types::IntoCow<'a, types::ConduitIdRef> + 'a) -> Self { + Self { + conduit_id: conduit_id.into_cow(), + status: None, + after: None, + } + } + + /// Filter shards by a specific status + pub fn status(mut self, status: eventsub::ShardStatus) -> Self { + self.status = Some(status); + self + } + + /// Set the cursor to get a page of results + pub fn after(mut self, after: impl types::IntoCow<'a, helix::CursorRef> + 'a) -> Self { + self.after = Some(after.into_cow()); + self + } +} + /// Return Values for [Get Conduit Shards](super::get_conduit_shards) /// /// [`get-conduit-shards`](https://dev.twitch.tv/docs/api/reference/#get-conduit-shards) @@ -59,12 +82,12 @@ impl helix::Paginated for GetConduitShardsRequest<'_> { #[test] fn test_uri() { use helix::*; - let req: GetConduitShardsRequest = GetConduitShardsRequest::default(); + let req = GetConduitShardsRequest::new("12345"); let uri = req.get_uri().unwrap(); assert_eq!( uri.to_string(), - "https://api.twitch.tv/helix/eventsub/conduits/shards?conduit_id=" + "https://api.twitch.tv/helix/eventsub/conduits/shards?conduit_id=12345" ); } @@ -77,7 +100,7 @@ fn test_request() { use crate::eventsub::{ ShardStatus, TransportResponse, WebhookTransportResponse, WebsocketTransportResponse, }; - let req: GetConduitShardsRequest = GetConduitShardsRequest::default(); + let req = GetConduitShardsRequest::new("12345"); let data = br#"{ "data": [ @@ -138,21 +161,21 @@ fn test_request() { response.data, vec![ crate::eventsub::ShardResponse { - id: "0".to_string(), + id: "0".into(), status: ShardStatus::Enabled, transport: TransportResponse::Webhook(WebhookTransportResponse { callback: "https://this-is-a-callback.com".to_string(), }), }, crate::eventsub::ShardResponse { - id: "1".to_string(), + id: "1".into(), status: ShardStatus::WebhookCallbackVerificationPending, transport: TransportResponse::Webhook(WebhookTransportResponse { callback: "https://this-is-a-callback-2.com".to_string(), }), }, crate::eventsub::ShardResponse { - id: "2".to_string(), + id: "2".into(), status: ShardStatus::Enabled, transport: TransportResponse::Websocket(WebsocketTransportResponse { session_id: "9fd5164a-a958-4c60-b7f4-6a7202506ca0".to_string(), @@ -161,7 +184,7 @@ fn test_request() { }), }, crate::eventsub::ShardResponse { - id: "3".to_string(), + id: "3".into(), status: ShardStatus::Enabled, transport: TransportResponse::Websocket(WebsocketTransportResponse { session_id: "238b4b08-13f1-4b8f-8d31-56665a7a9d9f".to_string(), @@ -170,7 +193,7 @@ fn test_request() { }), }, crate::eventsub::ShardResponse { - id: "4".to_string(), + id: "4".into(), status: ShardStatus::WebsocketDisconnected, transport: TransportResponse::Websocket(WebsocketTransportResponse { session_id: "ad1c9fc3-0d99-4eb7-8a04-8608e8ff9ec9".to_string(), diff --git a/src/helix/endpoints/eventsub/get_conduits.rs b/src/helix/endpoints/eventsub/get_conduits.rs index 9c99484e43..803a79de87 100644 --- a/src/helix/endpoints/eventsub/get_conduits.rs +++ b/src/helix/endpoints/eventsub/get_conduits.rs @@ -22,39 +22,7 @@ impl Request for GetConduitsRequest { const SCOPE: twitch_oauth2::Validator = twitch_oauth2::validator![]; } -impl RequestGet for GetConduitsRequest { - fn parse_inner_response( - request: Option, - uri: &http::Uri, - response: &str, - status: http::StatusCode, - ) -> Result, helix::HelixRequestGetError> - where - Self: Sized, - { - #[derive(PartialEq, Deserialize, Debug)] - #[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))] - struct InnerResponse { - data: Vec, - } - - let response: InnerResponse = helix::parse_json(response, true).map_err(|e| { - helix::HelixRequestGetError::DeserializeError( - response.to_string(), - e, - uri.clone(), - status, - ) - })?; - Ok(helix::Response::new( - response.data, - None, - request, - None, - None, - )) - } -} +impl RequestGet for GetConduitsRequest {} #[cfg(test)] #[test] @@ -98,11 +66,11 @@ fn test_request() { response.data, vec![ crate::eventsub::Conduit { - id: "26b1c993-bfcf-44d9-b876-379dacafe75a".to_string(), + id: "26b1c993-bfcf-44d9-b876-379dacafe75a".into(), shard_count: 15, }, crate::eventsub::Conduit { - id: "bfcfc993-26b1-b876-44d9-afe75a379dac".to_string(), + id: "bfcfc993-26b1-b876-44d9-afe75a379dac".into(), shard_count: 5, }, ] diff --git a/src/helix/endpoints/eventsub/update_conduit_shards.rs b/src/helix/endpoints/eventsub/update_conduit_shards.rs index 9211abd1c5..82139b9e1c 100644 --- a/src/helix/endpoints/eventsub/update_conduit_shards.rs +++ b/src/helix/endpoints/eventsub/update_conduit_shards.rs @@ -12,9 +12,13 @@ use helix::RequestPatch; #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[must_use] #[non_exhaustive] -pub struct UpdateConduitShardsRequest {} +pub struct UpdateConduitShardsRequest<'a> { + #[serde(skip)] + #[cfg_attr(feature = "typed-builder", builder(default))] + _phantom: std::marker::PhantomData<&'a ()>, +} -impl Request for UpdateConduitShardsRequest { +impl Request for UpdateConduitShardsRequest<'_> { type Response = UpdateConduitShardsResponse; const PATH: &'static str = "eventsub/conduits/shards"; @@ -41,25 +45,34 @@ pub struct UpdateConduitShardsResponse { #[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] #[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] #[non_exhaustive] -pub struct UpdateConduitShardsBody { +pub struct UpdateConduitShardsBody<'a> { /// Conduit ID. - pub conduit_id: String, + #[cfg_attr(feature = "typed-builder", builder(setter(into)))] + #[cfg_attr(feature = "deser_borrow", serde(borrow = "'a"))] + pub conduit_id: Cow<'a, types::ConduitIdRef>, /// List of shards to update. - pub shards: Vec, + #[cfg_attr(feature = "typed-builder", builder(setter(into)))] + pub shards: Cow<'a, [eventsub::Shard]>, } -impl UpdateConduitShardsBody { +impl<'a> UpdateConduitShardsBody<'a> { /// Conduit body settings - pub fn new(conduit_id: String, shards: Vec) -> Self { - Self { conduit_id, shards } + pub fn new( + conduit_id: impl types::IntoCow<'a, types::ConduitIdRef> + 'a, + shards: impl Into>, + ) -> Self { + Self { + conduit_id: conduit_id.into_cow(), + shards: shards.into(), + } } } -impl helix::private::SealedSerialize for UpdateConduitShardsBody {} +impl helix::private::SealedSerialize for UpdateConduitShardsBody<'_> {} -impl RequestPatch for UpdateConduitShardsRequest { - type Body = UpdateConduitShardsBody; +impl<'a> RequestPatch for UpdateConduitShardsRequest<'a> { + type Body = UpdateConduitShardsBody<'a>; fn parse_inner_response( request: Option, @@ -156,7 +169,7 @@ fn test_successful_response() { response.data.shards, vec![ crate::eventsub::ShardResponse { - id: "0".to_string(), + id: "0".into(), status: crate::eventsub::ShardStatus::Enabled, transport: crate::eventsub::TransportResponse::Webhook( crate::eventsub::WebhookTransportResponse { @@ -165,7 +178,7 @@ fn test_successful_response() { ), }, crate::eventsub::ShardResponse { - id: "1".to_string(), + id: "1".into(), status: crate::eventsub::ShardStatus::WebhookCallbackVerificationPending, transport: crate::eventsub::TransportResponse::Webhook( crate::eventsub::WebhookTransportResponse { @@ -179,7 +192,7 @@ fn test_successful_response() { assert_eq!( response.data.errors, vec![crate::eventsub::ShardError { - id: "3".to_string(), + id: "3".into(), message: "The shard id is outside the conduit's range".to_string(), code: "invalid_parameter".to_string(), },] From aebff1377501574677132fe3e89396c70ab9a84c Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Sun, 1 Dec 2024 12:20:02 +0100 Subject: [PATCH 2/4] feat(helix): add `Update Conduits` --- src/helix/client/client_ext.rs | 47 ++++++ src/helix/endpoints/eventsub/mod.rs | 7 +- .../endpoints/eventsub/update_conduit.rs | 142 ++++++++++++++++++ src/helix/mod.rs | 4 +- xtask/src/collect_endpoints/helix.rs | 1 + 5 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 src/helix/endpoints/eventsub/update_conduit.rs diff --git a/src/helix/client/client_ext.rs b/src/helix/client/client_ext.rs index 7b37857464..c36c3d305c 100644 --- a/src/helix/client/client_ext.rs +++ b/src/helix/client/client_ext.rs @@ -1800,6 +1800,53 @@ impl<'client, C: crate::HttpClient + Sync + 'client> HelixClient<'client, C> { make_stream(req, token, self, std::collections::VecDeque::from) } + #[cfg(feature = "eventsub")] + /// Updates a [conduit](crate::eventsub::Conduit)’s shard count.. + /// + /// # Notes + /// + /// To delete shards, update the count to a lower number, and the shards above the count will be deleted. + /// For example, if the existing shard count is 100, by resetting shard count to 50, shards 50-99 are disabled. + /// + /// The token must be an App Access Token. + /// + /// # Examples + /// + /// ```rust, no_run + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// # let client: helix::HelixClient<'static, twitch_api::client::DummyHttpClient> = helix::HelixClient::default(); + /// # let client_id = twitch_oauth2::types::ClientId::from_static("your_client_id"); + /// # let client_secret = twitch_oauth2::types::ClientSecret::from_static("your_client_id"); + /// # let token = twitch_oauth2::AppAccessToken::get_app_access_token(&client, client_id, client_secret, vec![]).await?; + /// use twitch_api::{helix, eventsub}; + /// + /// // The conduit ID of a previously created Conduit + /// let conduit_id = "bb7a1803-eb03-41ef-a1ab-e9242e72053e"; + /// let shard_count = 5; + /// let updated: eventsub::Conduit = client + /// .update_conduit(conduit_id, shard_count, &token) + /// .await?; + /// + /// # Ok(()) } + /// ``` + pub async fn update_conduit<'b: 'client, T>( + &'client self, + conduit_id: impl types::IntoCow<'b, types::ConduitIdRef> + 'b + Send, + shard_count: usize, + token: &'client T, + ) -> Result> + where + T: TwitchToken + Send + Sync + ?Sized, + { + let req = helix::eventsub::UpdateConduitRequest::default(); + let body = helix::eventsub::UpdateConduitBody::new(conduit_id, shard_count); + + self.req_patch(req, body, token) + .await + .map(|response| response.data) + } + #[cfg(feature = "eventsub")] /// Updates the [Shard](crate::eventsub) for the given [Conduit](crate::eventsub). /// diff --git a/src/helix/endpoints/eventsub/mod.rs b/src/helix/endpoints/eventsub/mod.rs index 8a57e3e5e5..7b70a5bbe0 100644 --- a/src/helix/endpoints/eventsub/mod.rs +++ b/src/helix/endpoints/eventsub/mod.rs @@ -4,13 +4,13 @@ //! //! //! -//!
Conduits 🟑 4/6 +//!
Conduits 🟑 5/6 //! //! | Endpoint | Helper | Module | //! |---|---|---| //! | [Get Conduits](https://dev.twitch.tv/docs/api/reference#get-conduits) | [`HelixClient::get_conduits`](crate::helix::HelixClient::get_conduits) | [`get_conduits`] | //! | [Create Conduits](https://dev.twitch.tv/docs/api/reference#create-conduits) | [`HelixClient::create_conduit`](crate::helix::HelixClient::create_conduit) | [`create_conduit`] | -//! | [Update Conduits](https://dev.twitch.tv/docs/api/reference#update-conduits) | - | - | +//! | [Update Conduits](https://dev.twitch.tv/docs/api/reference#update-conduits) | [`HelixClient::update_conduit`](crate::helix::HelixClient::update_conduit) | [`update_conduit`] | //! | [Delete Conduit](https://dev.twitch.tv/docs/api/reference#delete-conduit) | - | - | //! | [Get Conduit Shards](https://dev.twitch.tv/docs/api/reference#get-conduit-shards) | [`HelixClient::get_conduit_shards`](crate::helix::HelixClient::get_conduit_shards) | [`get_conduit_shards`] | //! | [Update Conduit Shards](https://dev.twitch.tv/docs/api/reference#update-conduit-shards) | [`HelixClient::update_conduit_shards`](crate::helix::HelixClient::update_conduit_shards) | [`update_conduit_shards`] | @@ -42,6 +42,7 @@ pub mod delete_eventsub_subscription; pub mod get_conduit_shards; pub mod get_conduits; pub mod get_eventsub_subscriptions; +pub mod update_conduit; pub mod update_conduit_shards; #[doc(inline)] @@ -61,6 +62,8 @@ pub use get_conduits::GetConduitsRequest; #[doc(inline)] pub use get_eventsub_subscriptions::{EventSubSubscriptions, GetEventSubSubscriptionsRequest}; #[doc(inline)] +pub use update_conduit::{UpdateConduitBody, UpdateConduitRequest}; +#[doc(inline)] pub use update_conduit_shards::{ UpdateConduitShardsBody, UpdateConduitShardsRequest, UpdateConduitShardsResponse, }; diff --git a/src/helix/endpoints/eventsub/update_conduit.rs b/src/helix/endpoints/eventsub/update_conduit.rs new file mode 100644 index 0000000000..2fba0eb97f --- /dev/null +++ b/src/helix/endpoints/eventsub/update_conduit.rs @@ -0,0 +1,142 @@ +//! Updates a conduit’s shard count. +//! [`update-conduit`](https://dev.twitch.tv/docs/api/reference/#update-conduits) +//! +//! To delete shards, update the count to a lower number, and the shards above the count will be deleted. +//! For example, if the existing shard count is 100, by resetting shard count to 50, shards 50-99 are disabled. + +use super::*; +use crate::eventsub; +use helix::RequestPatch; + +/// Query Parameters for [Update Conduit](super::update_conduit) +/// +/// [`update-conduit`](https://dev.twitch.tv/docs/api/reference/#update-conduits) +#[derive(PartialEq, Eq, Serialize, Clone, Debug, Default)] +#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] +#[must_use] +#[non_exhaustive] +pub struct UpdateConduitRequest<'a> { + #[serde(skip)] + #[cfg_attr(feature = "typed-builder", builder(default))] + _phantom: std::marker::PhantomData<&'a ()>, +} + +impl Request for UpdateConduitRequest<'_> { + type Response = eventsub::Conduit; + + const PATH: &'static str = "eventsub/conduits"; + #[cfg(feature = "twitch_oauth2")] + const SCOPE: twitch_oauth2::Validator = twitch_oauth2::validator![]; +} + +/// Body Parameters for [Update Conduit](super::update_conduit) +/// +/// [`update-conduit`](https://dev.twitch.tv/docs/api/reference/#update-conduits) +#[derive(PartialEq, Eq, Deserialize, Serialize, Clone, Debug)] +#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] +#[non_exhaustive] +pub struct UpdateConduitBody<'a> { + /// Conduit ID. + #[cfg_attr(feature = "typed-builder", builder(setter(into)))] + #[cfg_attr(feature = "deser_borrow", serde(borrow = "'a"))] + pub id: Cow<'a, types::ConduitIdRef>, + /// The new number of shards for this conduit. + pub shard_count: usize, +} + +impl helix::private::SealedSerialize for UpdateConduitBody<'_> {} + +impl<'a> UpdateConduitBody<'a> { + /// Conduit body settings + pub fn new(id: impl types::IntoCow<'a, types::ConduitIdRef> + 'a, shard_count: usize) -> Self { + Self { + id: id.into_cow(), + shard_count, + } + } +} + +impl<'a> RequestPatch for UpdateConduitRequest<'a> { + type Body = UpdateConduitBody<'a>; + + fn parse_inner_response( + request: Option, + uri: &http::Uri, + response: &str, + status: http::StatusCode, + ) -> Result, helix::HelixRequestPatchError> + where + Self: Sized, + { + helix::parse_single_return(request, uri, response, status) + } +} + +#[cfg(test)] +#[test] +fn test_uri() { + use helix::*; + let req = UpdateConduitRequest::default(); + + let uri = req.get_uri().unwrap(); + assert_eq!( + uri.to_string(), + "https://api.twitch.tv/helix/eventsub/conduits?" + ); +} + +#[cfg(test)] +#[test] +fn test_successful_response() { + use helix::*; + let req = UpdateConduitRequest::default(); + + let body = UpdateConduitBody::new("bfcfc993-26b1-b876-44d9-afe75a379dac", 5); + assert_eq!( + std::str::from_utf8(&body.try_to_body().unwrap()).unwrap(), + r#"{"id":"bfcfc993-26b1-b876-44d9-afe75a379dac","shard_count":5}"# + ); + + let data = br#"{ + "data": [ + { + "id": "bfcfc993-26b1-b876-44d9-afe75a379dac", + "shard_count": 5 + } + ] + } + "# + .to_vec(); + let http_response = http::Response::builder().status(200).body(data).unwrap(); + + let uri = req.get_uri().unwrap(); + let response = UpdateConduitRequest::parse_response(Some(req), &uri, http_response).unwrap(); + + assert_eq!( + response.data, + crate::eventsub::Conduit { + id: "bfcfc993-26b1-b876-44d9-afe75a379dac".into(), + shard_count: 5, + }, + ); + + dbg!("{:#?}", response); +} + +#[cfg(test)] +#[test] +fn test_successful_unexpected_response() { + use helix::*; + let req: UpdateConduitRequest = UpdateConduitRequest::default(); + + let data = br#"{ + "data": [] + } + "# + .to_vec(); + let http_response = http::Response::builder().status(200).body(data).unwrap(); + + let uri = req.get_uri().unwrap(); + let response = UpdateConduitRequest::parse_response(Some(req), &uri, http_response); + assert!(response.is_err()); +} diff --git a/src/helix/mod.rs b/src/helix/mod.rs index 479800c138..1f2012c050 100644 --- a/src/helix/mod.rs +++ b/src/helix/mod.rs @@ -136,13 +136,13 @@ //! //!
//! -//!
Conduits 🟑 4/6 +//!
Conduits 🟑 5/6 //! //! | Endpoint | Helper | Module | //! |---|---|---| //! | [Get Conduits](https://dev.twitch.tv/docs/api/reference#get-conduits) | [`HelixClient::get_conduits`] | [`eventsub::get_conduits`] | //! | [Create Conduits](https://dev.twitch.tv/docs/api/reference#create-conduits) | [`HelixClient::create_conduit`] | [`eventsub::create_conduit`] | -//! | [Update Conduits](https://dev.twitch.tv/docs/api/reference#update-conduits) | - | - | +//! | [Update Conduits](https://dev.twitch.tv/docs/api/reference#update-conduits) | [`HelixClient::update_conduit`] | [`eventsub::update_conduit`] | //! | [Delete Conduit](https://dev.twitch.tv/docs/api/reference#delete-conduit) | - | - | //! | [Get Conduit Shards](https://dev.twitch.tv/docs/api/reference#get-conduit-shards) | [`HelixClient::get_conduit_shards`] | [`eventsub::get_conduit_shards`] | //! | [Update Conduit Shards](https://dev.twitch.tv/docs/api/reference#update-conduit-shards) | [`HelixClient::update_conduit_shards`] | [`eventsub::update_conduit_shards`] | diff --git a/xtask/src/collect_endpoints/helix.rs b/xtask/src/collect_endpoints/helix.rs index b63888f74b..9857564a96 100644 --- a/xtask/src/collect_endpoints/helix.rs +++ b/xtask/src/collect_endpoints/helix.rs @@ -198,6 +198,7 @@ fn category_override(c: String) -> String { fn item_override(i: String) -> String { match i.as_str() { "create_conduits" => "create_conduit".to_owned(), + "update_conduits" => "update_conduit".to_owned(), "resolve_unban_requests" => "resolve_unban_request".to_owned(), "get_hype_train_events" => "get_hypetrain_events".to_owned(), _ => i, From 9ffc1df1475fcc00848acaae159d5406b93c2815 Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Sun, 1 Dec 2024 15:07:42 +0100 Subject: [PATCH 3/4] feat(helix): add `Delete Conduit` --- src/helix/client/client_ext.rs | 41 ++++++++ .../endpoints/eventsub/delete_conduit.rs | 98 +++++++++++++++++++ src/helix/endpoints/eventsub/mod.rs | 7 +- src/helix/mod.rs | 4 +- 4 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 src/helix/endpoints/eventsub/delete_conduit.rs diff --git a/src/helix/client/client_ext.rs b/src/helix/client/client_ext.rs index c36c3d305c..c7593d3330 100644 --- a/src/helix/client/client_ext.rs +++ b/src/helix/client/client_ext.rs @@ -1847,6 +1847,47 @@ impl<'client, C: crate::HttpClient + Sync + 'client> HelixClient<'client, C> { .map(|response| response.data) } + #[cfg(feature = "eventsub")] + /// Deletes a specified [conduit](crate::eventsub::Conduit). + /// + /// # Notes + /// + /// Note that it may take some time for Eventsub subscriptions on a deleted conduit to show as disabled when calling [Get Eventsub Subscriptions][Self::get_eventsub_subscriptions]. + /// + /// The token must be an App Access Token. + /// + /// # Examples + /// + /// ```rust, no_run + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// # let client: helix::HelixClient<'static, twitch_api::client::DummyHttpClient> = helix::HelixClient::default(); + /// # let client_id = twitch_oauth2::types::ClientId::from_static("your_client_id"); + /// # let client_secret = twitch_oauth2::types::ClientSecret::from_static("your_client_id"); + /// # let token = twitch_oauth2::AppAccessToken::get_app_access_token(&client, client_id, client_secret, vec![]).await?; + /// use twitch_api::{helix, eventsub}; + /// + /// // The conduit ID of a previously created Conduit + /// let conduit_id = "bb7a1803-eb03-41ef-a1ab-e9242e72053e"; + /// client + /// .delete_conduit(conduit_id, &token) + /// .await?; + /// + /// # Ok(()) } + /// ``` + pub async fn delete_conduit<'b: 'client, T>( + &'client self, + conduit_id: impl types::IntoCow<'b, types::ConduitIdRef> + 'b + Send, + token: &'client T, + ) -> Result<(), ClientError> + where + T: TwitchToken + Send + Sync + ?Sized, + { + let req = helix::eventsub::DeleteConduitRequest::new(conduit_id); + + self.req_delete(req, token).await.map(|_| ()) + } + #[cfg(feature = "eventsub")] /// Updates the [Shard](crate::eventsub) for the given [Conduit](crate::eventsub). /// diff --git a/src/helix/endpoints/eventsub/delete_conduit.rs b/src/helix/endpoints/eventsub/delete_conduit.rs new file mode 100644 index 0000000000..3488eaf9ab --- /dev/null +++ b/src/helix/endpoints/eventsub/delete_conduit.rs @@ -0,0 +1,98 @@ +//! Deletes a specified conduit. +//! [`delete-conduit`](https://dev.twitch.tv/docs/api/reference/#delete-conduit) +//! +//! Note that it may take some time for Eventsub subscriptions on a deleted conduit to show as disabled when calling [Get Eventsub Subscriptions](super::get_eventsub_subscriptions). + +use super::*; +use helix::RequestDelete; + +/// Query Parameters for [Delete Conduit](super::delete_conduit) +/// +/// [`delete-conduit`](https://dev.twitch.tv/docs/api/reference/#delete-conduit) +#[derive(PartialEq, Eq, Serialize, Clone, Debug)] +#[cfg_attr(feature = "typed-builder", derive(typed_builder::TypedBuilder))] +#[must_use] +#[non_exhaustive] +pub struct DeleteConduitRequest<'a> { + /// Conduit ID. + #[cfg_attr(feature = "typed-builder", builder(setter(into)))] + #[cfg_attr(feature = "deser_borrow", serde(borrow = "'a"))] + pub id: Cow<'a, types::ConduitIdRef>, +} + +impl<'a> DeleteConduitRequest<'a> { + /// Delete a specific conduit + pub fn new(id: impl types::IntoCow<'a, types::ConduitIdRef> + 'a) -> Self { + Self { id: id.into_cow() } + } +} + +/// Return Values for [Delete Conduit](super::delete_conduit) +/// +/// [`delete-conduit`](https://dev.twitch.tv/docs/api/reference/#delete-conduit) +#[derive(PartialEq, Eq, Deserialize, Serialize, Debug, Clone)] +#[non_exhaustive] +pub enum DeleteConduitResponse { + /// 204 - Conduit deleted + Success, +} + +impl Request for DeleteConduitRequest<'_> { + type Response = DeleteConduitResponse; + + const PATH: &'static str = "eventsub/conduits"; + #[cfg(feature = "twitch_oauth2")] + const SCOPE: twitch_oauth2::Validator = twitch_oauth2::validator![]; +} + +impl RequestDelete for DeleteConduitRequest<'_> { + fn parse_inner_response( + request: Option, + uri: &http::Uri, + response: &str, + status: http::StatusCode, + ) -> Result, helix::HelixRequestDeleteError> + where + Self: Sized, + { + match status { + http::StatusCode::NO_CONTENT | http::StatusCode::OK => Ok(helix::Response::with_data( + DeleteConduitResponse::Success, + request, + )), + _ => Err(helix::HelixRequestDeleteError::InvalidResponse { + reason: "unexpected status", + response: response.to_string(), + status, + uri: uri.clone(), + }), + } + } +} + +#[cfg(test)] +#[test] +fn test_uri() { + use helix::*; + let req = DeleteConduitRequest::new("bfcfc993-26b1-b876-44d9-afe75a379dac"); + + let uri = req.get_uri().unwrap(); + assert_eq!( + uri.to_string(), + "https://api.twitch.tv/helix/eventsub/conduits?id=bfcfc993-26b1-b876-44d9-afe75a379dac" + ); +} + +#[cfg(test)] +#[test] +fn test_successful_response() { + use helix::*; + let req = DeleteConduitRequest::new("bfcfc993-26b1-b876-44d9-afe75a379dac"); + + let http_response = http::Response::builder().status(204).body(vec![]).unwrap(); + + let uri = req.get_uri().unwrap(); + let response = DeleteConduitRequest::parse_response(Some(req), &uri, http_response).unwrap(); + + assert_eq!(response.data, DeleteConduitResponse::Success); +} diff --git a/src/helix/endpoints/eventsub/mod.rs b/src/helix/endpoints/eventsub/mod.rs index 7b70a5bbe0..d94f0a2c10 100644 --- a/src/helix/endpoints/eventsub/mod.rs +++ b/src/helix/endpoints/eventsub/mod.rs @@ -4,14 +4,14 @@ //! //! //! -//!
Conduits 🟑 5/6 +//!
Conduits 🟒 6/6 //! //! | Endpoint | Helper | Module | //! |---|---|---| //! | [Get Conduits](https://dev.twitch.tv/docs/api/reference#get-conduits) | [`HelixClient::get_conduits`](crate::helix::HelixClient::get_conduits) | [`get_conduits`] | //! | [Create Conduits](https://dev.twitch.tv/docs/api/reference#create-conduits) | [`HelixClient::create_conduit`](crate::helix::HelixClient::create_conduit) | [`create_conduit`] | //! | [Update Conduits](https://dev.twitch.tv/docs/api/reference#update-conduits) | [`HelixClient::update_conduit`](crate::helix::HelixClient::update_conduit) | [`update_conduit`] | -//! | [Delete Conduit](https://dev.twitch.tv/docs/api/reference#delete-conduit) | - | - | +//! | [Delete Conduit](https://dev.twitch.tv/docs/api/reference#delete-conduit) | [`HelixClient::delete_conduit`](crate::helix::HelixClient::delete_conduit) | [`delete_conduit`] | //! | [Get Conduit Shards](https://dev.twitch.tv/docs/api/reference#get-conduit-shards) | [`HelixClient::get_conduit_shards`](crate::helix::HelixClient::get_conduit_shards) | [`get_conduit_shards`] | //! | [Update Conduit Shards](https://dev.twitch.tv/docs/api/reference#update-conduit-shards) | [`HelixClient::update_conduit_shards`](crate::helix::HelixClient::update_conduit_shards) | [`update_conduit_shards`] | //! @@ -38,6 +38,7 @@ use std::borrow::Cow; pub mod create_conduit; pub mod create_eventsub_subscription; +pub mod delete_conduit; pub mod delete_eventsub_subscription; pub mod get_conduit_shards; pub mod get_conduits; @@ -52,6 +53,8 @@ pub use create_eventsub_subscription::{ CreateEventSubSubscription, CreateEventSubSubscriptionBody, CreateEventSubSubscriptionRequest, }; #[doc(inline)] +pub use delete_conduit::{DeleteConduitRequest, DeleteConduitResponse}; +#[doc(inline)] pub use delete_eventsub_subscription::{ DeleteEventSubSubscription, DeleteEventSubSubscriptionRequest, }; diff --git a/src/helix/mod.rs b/src/helix/mod.rs index 1f2012c050..c803c2f51e 100644 --- a/src/helix/mod.rs +++ b/src/helix/mod.rs @@ -136,14 +136,14 @@ //! //!
//! -//!
Conduits 🟑 5/6 +//!
Conduits 🟒 6/6 //! //! | Endpoint | Helper | Module | //! |---|---|---| //! | [Get Conduits](https://dev.twitch.tv/docs/api/reference#get-conduits) | [`HelixClient::get_conduits`] | [`eventsub::get_conduits`] | //! | [Create Conduits](https://dev.twitch.tv/docs/api/reference#create-conduits) | [`HelixClient::create_conduit`] | [`eventsub::create_conduit`] | //! | [Update Conduits](https://dev.twitch.tv/docs/api/reference#update-conduits) | [`HelixClient::update_conduit`] | [`eventsub::update_conduit`] | -//! | [Delete Conduit](https://dev.twitch.tv/docs/api/reference#delete-conduit) | - | - | +//! | [Delete Conduit](https://dev.twitch.tv/docs/api/reference#delete-conduit) | [`HelixClient::delete_conduit`] | [`eventsub::delete_conduit`] | //! | [Get Conduit Shards](https://dev.twitch.tv/docs/api/reference#get-conduit-shards) | [`HelixClient::get_conduit_shards`] | [`eventsub::get_conduit_shards`] | //! | [Update Conduit Shards](https://dev.twitch.tv/docs/api/reference#update-conduit-shards) | [`HelixClient::update_conduit_shards`] | [`eventsub::update_conduit_shards`] | //! From e668e54e1c2d38067805ac0bbb5833fb3f333989 Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Sun, 1 Dec 2024 15:19:15 +0100 Subject: [PATCH 4/4] fix(helix/conduits): make `shards` public --- src/helix/endpoints/eventsub/get_conduit_shards.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helix/endpoints/eventsub/get_conduit_shards.rs b/src/helix/endpoints/eventsub/get_conduit_shards.rs index 6d1d6bf369..b05f0fb74d 100644 --- a/src/helix/endpoints/eventsub/get_conduit_shards.rs +++ b/src/helix/endpoints/eventsub/get_conduit_shards.rs @@ -59,7 +59,7 @@ impl<'a> GetConduitShardsRequest<'a> { #[non_exhaustive] pub struct ConduitShards { /// List of information about a conduit's shards. - shards: Vec, + pub shards: Vec, } impl Request for GetConduitShardsRequest<'_> {