diff --git a/Cargo.lock b/Cargo.lock index bea3f0c8..2551254f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4904,6 +4904,23 @@ dependencies = [ "wp_serde_helper", ] +[[package]] +name = "wp_com_e2e" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "clap", + "colored", + "csv", + "futures", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "wp_api", +] + [[package]] name = "wp_contextual" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 58eb1791..5292fc20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "wp_api_integration_tests", "wp_api_integration_tests_backend", "wp_cli", + "wp_com_e2e", "wp_contextual", "wp_derive", "wp_derive_request_builder", diff --git a/native/swift/Sources/wordpress-api/Exports.swift b/native/swift/Sources/wordpress-api/Exports.swift index eda7b11b..3f1afc0b 100644 --- a/native/swift/Sources/wordpress-api/Exports.swift +++ b/native/swift/Sources/wordpress-api/Exports.swift @@ -149,3 +149,7 @@ public typealias CreateBotConversationParams = WordPressAPIInternal.CreateBotCon public typealias AddMessageToBotConversationParams = WordPressAPIInternal.AddMessageToBotConversationParams public typealias GetBotConversationParams = WordPressAPIInternal.GetBotConversationParams public typealias CreateBotConversationFeedbackParams = WordPressAPIInternal.CreateBotConversationFeedbackParams + +// MARK: Support Tickets +public typealias CreateSupportTicketParams = WordPressAPIInternal.CreateSupportTicketParams +public typealias AddMessageToSupportConversationParams = WordPressAPIInternal.AddMessageToSupportConversationParams diff --git a/native/swift/Sources/wordpress-api/WPComApiClient.swift b/native/swift/Sources/wordpress-api/WPComApiClient.swift index 2bdd408e..88c2fb80 100644 --- a/native/swift/Sources/wordpress-api/WPComApiClient.swift +++ b/native/swift/Sources/wordpress-api/WPComApiClient.swift @@ -25,4 +25,12 @@ public class WPComApiClient { public var supportBots: SupportBotsRequestExecutor { internalClient.supportBots() } + + public var supportEligibility: SupportEligibilityRequestExecutor { + internalClient.supportEligibility() + } + + public var supportTickets: SupportTicketsRequestExecutor { + internalClient.supportTickets() + } } diff --git a/wp_api/src/wp_com/client.rs b/wp_api/src/wp_com/client.rs index fd145eef..024e2b9b 100644 --- a/wp_api/src/wp_com/client.rs +++ b/wp_api/src/wp_com/client.rs @@ -5,6 +5,10 @@ use super::endpoint::{ oauth2::{Oauth2RequestBuilder, Oauth2RequestExecutor}, subscribers::{SubscribersRequestBuilder, SubscribersRequestExecutor}, support_bots_endpoint::{SupportBotsRequestBuilder, SupportBotsRequestExecutor}, + support_eligibility_endpoint::{ + SupportEligibilityRequestBuilder, SupportEligibilityRequestExecutor, + }, + support_tickets_endpoint::{SupportTicketsRequestBuilder, SupportTicketsRequestExecutor}, }; use crate::{ WpApiClientDelegate, api_client_generate_api_client, api_client_generate_endpoint_impl, @@ -33,6 +37,8 @@ pub struct WpComApiRequestBuilder { oauth2: Arc, subscribers: Arc, support_bots: Arc, + support_eligibility: Arc, + support_tickets: Arc, } impl WpComApiRequestBuilder { @@ -45,7 +51,9 @@ impl WpComApiRequestBuilder { jetpack_connection, oauth2, subscribers, - support_bots + support_bots, + support_eligibility, + support_tickets ) } } @@ -70,6 +78,8 @@ pub struct WpComApiClient { oauth2: Arc, subscribers: Arc, support_bots: Arc, + support_eligibility: Arc, + support_tickets: Arc, } impl WpComApiClient { @@ -83,7 +93,9 @@ impl WpComApiClient { jetpack_connection, oauth2, subscribers, - support_bots + support_bots, + support_eligibility, + support_tickets ) } } @@ -91,3 +103,5 @@ api_client_generate_endpoint_impl!(WpComApi, jetpack_connection); api_client_generate_endpoint_impl!(WpComApi, oauth2); api_client_generate_endpoint_impl!(WpComApi, subscribers); api_client_generate_endpoint_impl!(WpComApi, support_bots); +api_client_generate_endpoint_impl!(WpComApi, support_eligibility); +api_client_generate_endpoint_impl!(WpComApi, support_tickets); diff --git a/wp_api/src/wp_com/endpoint.rs b/wp_api/src/wp_com/endpoint.rs index 0ed0f034..df826fd3 100644 --- a/wp_api/src/wp_com/endpoint.rs +++ b/wp_api/src/wp_com/endpoint.rs @@ -10,6 +10,8 @@ pub mod jetpack_connection_endpoint; pub mod oauth2; pub mod subscribers; pub mod support_bots_endpoint; +pub mod support_eligibility_endpoint; +pub mod support_tickets_endpoint; #[derive(uniffi::Object)] pub struct WpComDotOrgApiUrlResolver { diff --git a/wp_api/src/wp_com/endpoint/support_eligibility_endpoint.rs b/wp_api/src/wp_com/endpoint/support_eligibility_endpoint.rs new file mode 100644 index 00000000..07f09590 --- /dev/null +++ b/wp_api/src/wp_com/endpoint/support_eligibility_endpoint.rs @@ -0,0 +1,18 @@ +use wp_derive_request_builder::WpDerivedRequest; + +use crate::{ + request::endpoint::{AsNamespace, DerivedRequest}, + wp_com::{WpComNamespace, support_eligibility::SupportEligibility}, +}; + +#[derive(WpDerivedRequest)] +enum SupportEligibilityRequest { + #[get(url = "/mobile-support/eligibility", output = SupportEligibility)] + GetSupportEligibility, +} + +impl DerivedRequest for SupportEligibilityRequest { + fn namespace() -> impl AsNamespace { + WpComNamespace::V2 + } +} diff --git a/wp_api/src/wp_com/endpoint/support_tickets_endpoint.rs b/wp_api/src/wp_com/endpoint/support_tickets_endpoint.rs new file mode 100644 index 00000000..bdc19b3a --- /dev/null +++ b/wp_api/src/wp_com/endpoint/support_tickets_endpoint.rs @@ -0,0 +1,29 @@ +use crate::{ + request::endpoint::{AsNamespace, DerivedRequest}, + wp_com::{ + WpComNamespace, + support_tickets::{ + AddMessageToSupportConversationParams, ConversationId, CreateSupportTicketParams, + SupportConversation, SupportConversationSummary, + }, + }, +}; +use wp_derive_request_builder::WpDerivedRequest; + +#[derive(WpDerivedRequest)] +enum SupportTicketsRequest { + #[post(url = "/mobile-support/conversations", params = &CreateSupportTicketParams, output = SupportConversation)] + CreateSupportTicket, + #[get(url = "/mobile-support/conversations", output = Vec)] + GetSupportConversationList, + #[get(url = "/mobile-support/conversations/", output = SupportConversation)] + GetSupportConversation, + #[post(url = "/mobile-support/conversations/", params = &AddMessageToSupportConversationParams, output = SupportConversation)] + AddMessageToSupportConversation, +} + +impl DerivedRequest for SupportTicketsRequest { + fn namespace() -> impl AsNamespace { + WpComNamespace::V2 + } +} diff --git a/wp_api/src/wp_com/mod.rs b/wp_api/src/wp_com/mod.rs index 252cbc34..87f07537 100644 --- a/wp_api/src/wp_com/mod.rs +++ b/wp_api/src/wp_com/mod.rs @@ -8,6 +8,8 @@ pub mod jetpack_connection; pub mod oauth2; pub mod subscribers; pub mod support_bots; +pub mod support_eligibility; +pub mod support_tickets; impl_as_query_value_for_new_type!(WpComSiteId); uniffi::custom_newtype!(WpComSiteId, u64); diff --git a/wp_api/src/wp_com/support_eligibility.rs b/wp_api/src/wp_com/support_eligibility.rs new file mode 100644 index 00000000..3b25a1c8 --- /dev/null +++ b/wp_api/src/wp_com/support_eligibility.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)] +pub struct SupportEligibility { + pub is_user_eligible: bool, +} diff --git a/wp_api/src/wp_com/support_tickets.rs b/wp_api/src/wp_com/support_tickets.rs new file mode 100644 index 00000000..324ce334 --- /dev/null +++ b/wp_api/src/wp_com/support_tickets.rs @@ -0,0 +1,145 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::{date::WpGmtDateTime, impl_as_query_value_for_new_type}; + +#[derive(Debug, PartialEq, Eq, Serialize, uniffi::Record)] +pub struct CreateSupportTicketParams { + pub subject: String, + pub message: String, + pub application: String, + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub wpcom_site_id: Option, + #[uniffi(default = [])] + pub tags: Vec, + #[uniffi(default = [])] + pub attachments: Vec, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)] +pub struct SupportConversationSummary { + pub id: ConversationId, + pub title: String, + pub description: String, + pub status: String, + pub created_at: WpGmtDateTime, + pub updated_at: WpGmtDateTime, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)] +pub struct SupportConversation { + pub id: ConversationId, + pub title: String, + pub description: String, + pub status: String, + pub created_at: WpGmtDateTime, + pub updated_at: WpGmtDateTime, + pub messages: Vec, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)] +pub struct SupportMessage { + pub id: u64, + pub content: String, + pub author: SupportMessageAuthor, + pub role: String, + pub author_is_current_user: bool, + pub created_at: WpGmtDateTime, + pub attachments: Vec, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)] +pub struct SupportUserIdentity { + pub id: u64, + pub email: String, + pub display_name: String, + pub avatar_url: String, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)] +pub struct SupportAttachment { + pub id: u64, + pub filename: String, + pub content_type: String, + pub size: u64, + pub url: String, + pub metadata: HashMap, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)] +#[serde(untagged)] +pub enum SupportMessageAuthor { + User(SupportUserIdentity), + SupportAgent(SupportAgentIdentity), +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum, strum_macros::Display)] +#[strum(serialize_all = "snake_case")] +pub enum AttachmentMetadataKey { + Width, + Height, + Other(String), +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)] +#[serde(untagged)] +pub enum AttachmentMetadataValue { + String(String), + Number(u64), + Boolean(bool), +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)] +pub struct SupportAgentIdentity { + pub id: u64, + pub name: String, +} + +#[derive(Debug, PartialEq, Eq, Serialize, uniffi::Record)] +pub struct AddMessageToSupportConversationParams { + pub message: String, + #[uniffi(default = [])] + pub attachments: Vec, +} + +impl_as_query_value_for_new_type!(ConversationId); +uniffi::custom_newtype!(ConversationId, u64); +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub struct ConversationId(pub u64); + +impl std::str::FromStr for ConversationId { + type Err = std::num::ParseIntError; + + fn from_str(s: &str) -> Result { + s.parse().map(Self) + } +} + +impl std::fmt::Display for ConversationId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_support_conversation_deserialization() { + let json = include_str!("../../tests/wpcom/support_tickets/single-conversation.json"); + let conversation: SupportConversation = + serde_json::from_str(json).expect("Failed to deserialize support conversation"); + assert_eq!(conversation.messages.len(), 7); + } + + #[test] + fn test_support_conversation_list_deserialization() { + let json = include_str!("../../tests/wpcom/support_tickets/conversation-list.json"); + let conversation_list: Vec = + serde_json::from_str(json).expect("Failed to deserialize support conversation list"); + assert_eq!(conversation_list.len(), 11); + } +} diff --git a/wp_api/tests/wpcom/support_tickets/conversation-list.json b/wp_api/tests/wpcom/support_tickets/conversation-list.json new file mode 100644 index 00000000..f07097a8 --- /dev/null +++ b/wp_api/tests/wpcom/support_tickets/conversation-list.json @@ -0,0 +1,90 @@ +[ + { + "id": 9033503, + "title": "🎨 Theme Customization Help", + "description": "Hello! 👋\n\nI see you're having trouble with your theme customization. Let me help you with that!\n\nThe color picker issue you're experiencing is a known bug that we've fixed in the latest update. Please try updating your theme to version 2.3.1.\n\nIf you need any further assistance, I'm here to help!\n\nBest regards,\n\nTheme Support Team 🌟", + "status": "closed", + "created_at": "2024-03-15T14:30:22+00:00", + "updated_at": "2024-03-18T09:45:33+00:00" + }, + { + "id": 1698812, + "title": "🔒 Security Update Required", + "description": "مرحباً!\n\nلقد لاحظنا أن موقعك يحتاج إلى تحديث أمني. يرجى تحديث إصدار WordPress الخاص بك إلى أحدث إصدار متاح.\n\nيمكنك القيام بذلك من لوحة التحكم الخاصة بك.\n\nمع تحياتنا،\n\nفريق الدعم الأمني", + "status": "closed", + "created_at": "2024-03-10T09:15:45+00:00", + "updated_at": "2024-03-11T16:20:12+00:00" + }, + { + "id": 1629241, + "title": "🚀 Performance Optimization", + "description": "Bonjour!\n\nJe vois que vous souhaitez optimiser les performances de votre site. Voici quelques suggestions:\n\n1. Activez la mise en cache\n2. Optimisez vos images\n3. Utilisez un CDN\n\nVoulez-vous que je vous guide à travers ces étapes?\n\nCordialement,\n\nL'équipe de support", + "status": "closed", + "created_at": "2024-03-05T11:22:18+00:00", + "updated_at": "2024-03-07T14:33:29+00:00" + }, + { + "id": 1500710, + "title": "📱 Mobile App Feedback", + "description": "Hi there! 👋\n\nThank you for your feedback about our mobile app. We're constantly working to improve the user experience.\n\nYour suggestion about the navigation menu has been forwarded to our development team.\n\nKeep the feedback coming! 💪\n\nBest regards,\n\nMobile Support Team", + "status": "closed", + "created_at": "2024-02-28T15:40:55+00:00", + "updated_at": "2024-03-01T10:15:22+00:00" + }, + { + "id": 1499869, + "title": "🎯 SEO Optimization Help", + "description": "Hello!\n\nI'd be happy to help you with your SEO optimization. Here are some key areas we can focus on:\n\n1. Meta descriptions\n2. Image alt tags\n3. URL structure\n\nWould you like me to provide more specific guidance on any of these topics?\n\nBest regards,\n\nSEO Support Team", + "status": "closed", + "created_at": "2024-02-25T13:20:33+00:00", + "updated_at": "2024-02-27T09:45:11+00:00" + }, + { + "id": 1500203, + "title": "💰 Payment Gateway Setup", + "description": "Hi!\n\nI see you're setting up a new payment gateway. Let's make sure everything is configured correctly.\n\nPlease verify these settings:\n1. API keys\n2. Webhook endpoints\n3. Test mode status\n\nNeed any clarification? 🤔\n\nBest regards,\n\nPayment Support Team", + "status": "closed", + "created_at": "2024-02-20T10:15:44+00:00", + "updated_at": "2024-02-22T16:30:27+00:00" + }, + { + "id": 1698812, + "title": "🎨 Design Consultation", + "description": "Hello! 👋\n\nThank you for reaching out about your site's design. I'd be happy to help you achieve the look you're going for.\n\nWould you like to schedule a design consultation call? We can discuss:\n- Color schemes\n- Layout options\n- Typography\n\nLet me know what works best for you! ✨\n\nBest regards,\n\nDesign Support Team", + "status": "closed", + "created_at": "2024-02-15T09:30:22+00:00", + "updated_at": "2024-02-17T14:45:33+00:00" + }, + { + "id": 1629241, + "title": "📊 Analytics Integration", + "description": "Hi there!\n\nI see you're having trouble with your analytics integration. Let's get that sorted out.\n\nFirst, please verify that you've added the tracking code correctly to your site's header.\n\nNeed help finding where to add it? 🧐\n\nBest regards,\n\nAnalytics Support Team", + "status": "closed", + "created_at": "2024-02-10T11:20:18+00:00", + "updated_at": "2024-02-12T15:33:29+00:00" + }, + { + "id": 1500710, + "title": "🌐 Multilingual Setup", + "description": "Bonjour!\n\nJe vois que vous souhaitez configurer votre site en plusieurs langues. Voici les étapes à suivre:\n\n1. Installer le plugin de traduction\n2. Configurer les langues\n3. Traduire le contenu\n\nBesoin d'aide avec l'une de ces étapes? 🤝\n\nCordialement,\n\nL'équipe de support multilingue", + "status": "closed", + "created_at": "2024-02-05T13:40:55+00:00", + "updated_at": "2024-02-07T10:15:22+00:00" + }, + { + "id": 1499869, + "title": "🔧 Technical Support", + "description": "Hello! 👋\n\nI understand you're experiencing some technical issues. Let's troubleshoot this step by step.\n\nCould you please:\n1. Clear your browser cache\n2. Try a different browser\n3. Check your internet connection\n\nLet me know if you need help with any of these steps! 🛠️\n\nBest regards,\n\nTechnical Support Team", + "status": "closed", + "created_at": "2024-02-01T10:20:33+00:00", + "updated_at": "2024-02-03T09:45:11+00:00" + }, + { + "id": 1500203, + "title": "📝 Content Migration", + "description": "Hi there!\n\nI see you're planning to migrate your content. Let me help you with that process.\n\nWe can use our automated migration tool, or I can guide you through a manual migration.\n\nWhich option would you prefer? 🤔\n\nBest regards,\n\nMigration Support Team", + "status": "closed", + "created_at": "2024-01-28T09:15:44+00:00", + "updated_at": "2024-01-30T16:30:27+00:00" + } + ] diff --git a/wp_api/tests/wpcom/support_tickets/single-conversation.json b/wp_api/tests/wpcom/support_tickets/single-conversation.json new file mode 100644 index 00000000..40ada997 --- /dev/null +++ b/wp_api/tests/wpcom/support_tickets/single-conversation.json @@ -0,0 +1,131 @@ +{ + "id": 9796958, + "title": "Issue with Jetpack Connection", + "description": "Having trouble connecting Jetpack to my site", + "status": "open", + "messages": [ + { + "id": 37691022090900, + "content": "I'm having trouble connecting Jetpack to my WordPress site. The connection keeps failing and I'm not sure what to do.", + "role": "user", + "author": { + "id": 375621849651, + "name": "Test User", + "email": "test.user@example.com" + }, + "author_is_current_user": true, + "created_at": "2025-05-21T01:03:11+00:00", + "attachments": [] + }, + { + "id": 37691118978324, + "content": "Hi Test User,\n\nI'm sorry to hear you're having trouble with your Jetpack connection. Could you please try disconnecting and reconnecting Jetpack from your site's dashboard? Also, please check if your site's XML-RPC is enabled.\n\nSupport Agent\nSupport Agent\n**| Automattic | WordPress.com**", + "role": "support", + "author": { + "id": 370234131652, + "name": "Support Agent" + }, + "author_is_current_user": false, + "created_at": "2025-05-21T01:05:27+00:00", + "attachments": [] + }, + { + "id": 37691524437652, + "content": "I've tried disconnecting and reconnecting, but I'm still seeing the same error message. Here's what I see on my screen.\n\nSupport Agent\nSupport Agent\n**| Automattic | WordPress.com**", + "role": "support", + "author": { + "id": 370234131652, + "name": "Support Agent" + }, + "author_is_current_user": false, + "created_at": "2025-05-21T01:35:23+00:00", + "attachments": [] + }, + { + "id": 37691573997588, + "content": "Could you please check if your site's firewall or security plugins might be blocking the connection? Sometimes these can interfere with Jetpack's connection process.\n\nSupport Agent\nSupport Agent\n**| Automattic | WordPress.com**", + "role": "support", + "author": { + "id": 370234131652, + "name": "Support Agent" + }, + "author_is_current_user": false, + "created_at": "2025-05-21T01:37:51+00:00", + "attachments": [] + }, + { + "id": 37725928573972, + "content": "I've attached a screenshot of the error message I'm seeing. It says 'Connection failed' with error code 403.", + "role": "user", + "author": { + "id": 375621849651, + "name": "Test User", + "email": "test.user@example.com" + }, + "author_is_current_user": true, + "created_at": "2025-05-22T00:04:48+00:00", + "attachments": [ + { + "id": 37725924561684, + "filename": "test_screenshot_1.jpeg", + "content_type": "image\/jpeg", + "size": 246171, + "url": "https:\/\/a8c.zendesk.com\/attachments\/token\/nZ1I5S7NkM8J8lpf9wAA9V6BL\/?name=test_screenshot_1.jpeg", + "metadata": { + "width": 1179, + "height": 2556 + } + } + ] + }, + { + "id": 37726042281364, + "content": "Here's another screenshot showing my site's security settings. I've temporarily disabled the firewall to test the connection.", + "role": "user", + "author": { + "id": 375621849651, + "name": "Test User", + "email": "test.user@example.com" + }, + "author_is_current_user": true, + "created_at": "2025-05-22T00:11:50+00:00", + "attachments": [ + { + "id": 37726025831316, + "filename": "test_screenshot_2.jpeg", + "content_type": "image\/jpeg", + "size": 246171, + "url": "https:\/\/a8c.zendesk.com\/attachments\/token\/nGyuwTgotHH3HAnm6WOCmfXuP\/?name=test_screenshot_2.jpeg", + "metadata": { + "width": 1179, + "height": 2556 + } + } + ] + }, + { + "id": 37726222988180, + "content": "I've recorded a video showing the connection process and the error that occurs. You can see exactly what happens when I try to connect.", + "role": "user", + "author": { + "id": 375621849651, + "name": "Test User", + "email": "test.user@example.com" + }, + "author_is_current_user": true, + "created_at": "2025-05-22T00:22:09+00:00", + "attachments": [ + { + "id": 37726236284948, + "filename": "test_recording_1.mp4", + "content_type": "video\/mp4", + "size": 25897659, + "url": "https:\/\/a8c.zendesk.com\/attachments\/token\/pxM2QJE7errXpreLxEgFA4w5L\/?name=test_recording_1.mp4", + "metadata": {} + } + ] + } + ], + "created_at": "2025-05-21T01:03:10+00:00", + "updated_at": "2025-05-22T00:22:09+00:00" +} diff --git a/wp_com_e2e/Cargo.toml b/wp_com_e2e/Cargo.toml new file mode 100644 index 00000000..bf8ee15d --- /dev/null +++ b/wp_com_e2e/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "wp_com_e2e" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = { workspace = true } +async-trait = { workspace = true } +clap = { workspace = true, features = ["derive", "env"] } +colored = { workspace = true } +csv = { workspace = true } +futures = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +tokio = { workspace = true, features = ["full"] } +tokio-stream = { workspace = true } +wp_api = { path = "../wp_api", features = [ "reqwest-request-executor" ] } + +[[bin]] +name = "wp_com_e2e" diff --git a/wp_com_e2e/src/main.rs b/wp_com_e2e/src/main.rs new file mode 100644 index 00000000..f747f1d8 --- /dev/null +++ b/wp_com_e2e/src/main.rs @@ -0,0 +1,82 @@ +use anyhow::Result; +use async_trait::async_trait; +use clap::{Parser, Subcommand}; +use oauth2_tests::Oauth2Test; +use std::sync::Arc; +use support_eligibility_test::SupportEligibilityTest; +use support_tickets_test::SupportTicketsTest; +use wp_api::{ + WpApiClientDelegate, WpAppNotifier, + auth::{WpAuthentication, WpAuthenticationProvider}, + middleware::WpApiMiddlewarePipeline, + reqwest_request_executor::ReqwestRequestExecutor, + wp_com::client::WpComApiClient, +}; + +mod oauth2_tests; +mod support_eligibility_test; +mod support_tickets_test; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + Test { + #[arg(short = 't', long = "token", env = "WP_COM_API_KEY")] + token: String, + }, +} + +#[async_trait] +trait Testable { + async fn test(&self) -> Result<(), anyhow::Error>; +} + +#[derive(Debug)] +pub struct EmptyAppNotifier; + +#[async_trait] +impl WpAppNotifier for EmptyAppNotifier { + async fn requested_with_invalid_authentication(&self) { + // no-op + } +} + +#[tokio::main] +async fn main() -> Result<(), anyhow::Error> { + let cli = Cli::parse(); + + match cli.command { + Commands::Test { token } => { + let delegate = WpApiClientDelegate { + auth_provider: WpAuthenticationProvider::static_with_auth( + WpAuthentication::Bearer { + token: token.clone(), + }, + ) + .into(), + request_executor: Arc::new(ReqwestRequestExecutor::default()), + middleware_pipeline: Arc::new(WpApiMiddlewarePipeline::default()), + app_notifier: Arc::new(EmptyAppNotifier), + }; + + let client = WpComApiClient::new(delegate); + + Oauth2Test { + client: &client, + token: &token, + } + .test() + .await?; + SupportTicketsTest { client: &client }.test().await?; + SupportEligibilityTest { client: &client }.test().await?; + } + } + + Ok(()) +} diff --git a/wp_com_e2e/src/oauth2_tests.rs b/wp_com_e2e/src/oauth2_tests.rs new file mode 100644 index 00000000..85b4f872 --- /dev/null +++ b/wp_com_e2e/src/oauth2_tests.rs @@ -0,0 +1,27 @@ +use anyhow::{Ok, Result}; +use async_trait::async_trait; +use wp_api::wp_com::{client::WpComApiClient, oauth2::TokenValidationParameters}; + +use crate::Testable; + +pub struct Oauth2Test<'a> { + pub token: &'a String, + pub client: &'a WpComApiClient, +} + +#[async_trait] +impl Testable for Oauth2Test<'_> { + async fn test(&self) -> Result<(), anyhow::Error> { + println!("== OAuth 2 Token Test =="); + self.client + .oauth2() + .fetch_info(&TokenValidationParameters { + client_id: "11".to_string(), + token: self.token.clone(), + }) + .await?; + println!("✅ Get OAuth 2 Token Info"); + + Ok(()) + } +} diff --git a/wp_com_e2e/src/support_eligibility_test.rs b/wp_com_e2e/src/support_eligibility_test.rs new file mode 100644 index 00000000..246fb06c --- /dev/null +++ b/wp_com_e2e/src/support_eligibility_test.rs @@ -0,0 +1,23 @@ +use anyhow::{Ok, Result}; +use async_trait::async_trait; +use wp_api::wp_com::client::WpComApiClient; + +use crate::Testable; + +pub struct SupportEligibilityTest<'a> { + pub client: &'a WpComApiClient, +} + +#[async_trait] +impl Testable for SupportEligibilityTest<'_> { + async fn test(&self) -> Result<(), anyhow::Error> { + println!("== Support Eligibility Test =="); + self.client + .support_eligibility() + .get_support_eligibility() + .await?; + println!("✅ Get Support Eligibility"); + + Ok(()) + } +} diff --git a/wp_com_e2e/src/support_tickets_test.rs b/wp_com_e2e/src/support_tickets_test.rs new file mode 100644 index 00000000..33bcbaac --- /dev/null +++ b/wp_com_e2e/src/support_tickets_test.rs @@ -0,0 +1,59 @@ +use anyhow::{Ok, Result}; +use async_trait::async_trait; +use wp_api::wp_com::{ + client::WpComApiClient, + support_tickets::{AddMessageToSupportConversationParams, CreateSupportTicketParams}, +}; + +use crate::Testable; + +pub struct SupportTicketsTest<'a> { + pub client: &'a WpComApiClient, +} + +#[async_trait] +impl Testable for SupportTicketsTest<'_> { + async fn test(&self) -> Result<(), anyhow::Error> { + println!("== Support Tickets Test =="); + let new_conversation = self + .client + .support_tickets() + .create_support_ticket(&CreateSupportTicketParams { + subject: "Mobile Support Test Message".to_string(), + message: "This is a test – it can be deleted without replying.".to_string(), + application: "jetpack".to_string(), + wpcom_site_id: None, + tags: vec!["jetpack_mobile".to_string(), "test".to_string()], + attachments: vec![], + }) + .await? + .data; + println!("✅ Create Conversation"); + + self.client + .support_tickets() + .add_message_to_support_conversation( + &new_conversation.id, + &AddMessageToSupportConversationParams { + message: "Test Message".to_string(), + attachments: vec![], + }, + ) + .await?; + println!("✅ Add Message to Conversation"); + + self.client + .support_tickets() + .get_support_conversation(&new_conversation.id) + .await?; + println!("✅ Fetch Conversation"); + + self.client + .support_tickets() + .get_support_conversation_list() + .await?; + println!("✅ Fetch Conversation List"); + + Ok(()) + } +}