From c83dfe05a7322ca60e39e48b5077606a6681d829 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 15 May 2024 14:11:24 -0400 Subject: [PATCH 01/24] Adds SparsePlugin --- wp_api/src/lib.rs | 1 + wp_api/src/plugins.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 wp_api/src/plugins.rs diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index eef523548..48644e3db 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -15,6 +15,7 @@ pub mod api_error; pub mod endpoint; pub mod login; pub mod pages; +pub mod plugins; pub mod posts; pub mod url; pub mod users; diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs new file mode 100644 index 000000000..d2db92054 --- /dev/null +++ b/wp_api/src/plugins.rs @@ -0,0 +1,43 @@ +use serde::{Deserialize, Serialize}; +use wp_derive::WPContextual; + +#[derive(Debug, Serialize, Deserialize, uniffi::Record, WPContextual)] +pub struct SparsePlugin { + #[WPContext(edit, embed, view)] + pub plugin: Option, + #[WPContext(edit, embed, view)] + pub status: Option, + #[WPContext(edit, embed, view)] + pub name: Option, + #[WPContext(edit, view)] + // TODO: Custom URI type? + pub plugin_uri: Option, + #[WPContext(edit, view)] + pub author: Option, + #[WPContext(edit, view)] + pub description: Option, + #[WPContext(edit, view)] + pub version: Option, + #[WPContext(edit, embed, view)] + pub network_only: Option, + #[WPContext(edit, embed, view)] + pub requires_php: Option, + #[WPContext(edit, view)] + pub textdomain: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, uniffi::Enum)] +pub enum PluginStatus { + Active, + Inactive, +} + +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] +pub struct PluginAuthor { + pub name: Option, +} + +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] +pub struct PluginDescription { + pub name: Option, +} From 79ce0942d79d06710f94562b15162913160e34d8 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 15 May 2024 14:18:07 -0400 Subject: [PATCH 02/24] Adds plugins list endpoint --- wp_api/src/endpoint.rs | 4 ++ wp_api/src/endpoint/plugins_endpoint.rs | 60 +++++++++++++++++++++++++ wp_api/src/plugins.rs | 29 ++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 wp_api/src/endpoint/plugins_endpoint.rs diff --git a/wp_api/src/endpoint.rs b/wp_api/src/endpoint.rs index fc707f149..100de14ec 100644 --- a/wp_api/src/endpoint.rs +++ b/wp_api/src/endpoint.rs @@ -1,7 +1,9 @@ use url::Url; +pub use plugins_endpoint::*; pub use users_endpoint::*; +mod plugins_endpoint; mod users_endpoint; const WP_JSON_PATH_SEGMENTS: [&str; 3] = ["wp-json", "wp", "v2"]; @@ -47,6 +49,7 @@ impl ApiBaseUrl { pub struct ApiEndpoint { pub base_url: ApiBaseUrl, pub users: UsersEndpoint, + pub plugins: PluginsEndpoint, } impl ApiEndpoint { @@ -54,6 +57,7 @@ impl ApiEndpoint { Self { base_url: api_base_url.clone(), users: UsersEndpoint::new(api_base_url.clone()), + plugins: PluginsEndpoint::new(api_base_url.clone()), } } diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs new file mode 100644 index 000000000..6361b40a9 --- /dev/null +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -0,0 +1,60 @@ +use url::Url; + +use crate::{plugins::PluginListParams, ApiBaseUrl, WPContext}; + +pub struct PluginsEndpoint { + api_base_url: ApiBaseUrl, +} + +impl PluginsEndpoint { + pub fn new(api_base_url: ApiBaseUrl) -> Self { + Self { api_base_url } + } + + pub fn list(&self, context: WPContext, params: Option<&PluginListParams>) -> Url { + let mut url = self.api_base_url.by_appending("plugins"); + url.query_pairs_mut() + .append_pair("context", context.as_str()); + if let Some(params) = params { + url.query_pairs_mut().extend_pairs(params.query_pairs()); + } + url + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{plugins::PluginStatus, ApiEndpoint}; + use rstest::*; + + #[rstest] + fn list_plugins_with_params(api_base_url: ApiBaseUrl, plugins_endpoint: PluginsEndpoint) { + let params = PluginListParams { + search: Some("foo".to_string()), + status: Some(PluginStatus::Active), + }; + validate_endpoint( + plugins_endpoint.list(WPContext::Edit, Some(¶ms)), + "/plugins?context=edit&search=foo&status=active", + &api_base_url, + ); + } + + #[fixture] + fn api_base_url() -> ApiBaseUrl { + ApiBaseUrl::new("https://foo.com").unwrap() + } + + #[fixture] + fn plugins_endpoint(api_base_url: ApiBaseUrl) -> PluginsEndpoint { + ApiEndpoint::new(api_base_url).plugins + } + + fn validate_endpoint(endpoint_url: Url, path: &str, api_base_url: &ApiBaseUrl) { + assert_eq!( + endpoint_url.as_str(), + format!("{}{}", api_base_url.as_str(), path) + ); + } +} diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index d2db92054..53bb37e0c 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -1,6 +1,26 @@ use serde::{Deserialize, Serialize}; use wp_derive::WPContextual; +#[derive(Default, Debug, uniffi::Record)] +pub struct PluginListParams { + /// Limit results to those matching a string. + pub search: Option, + /// Limits results to plugins with the given status. + pub status: Option, +} + +impl PluginListParams { + pub fn query_pairs(&self) -> impl IntoIterator { + [ + ("search", self.search.clone()), + ("status", self.status.map(|x| x.as_str().to_string())), + ] + .into_iter() + // Remove `None` values + .filter_map(|(k, opt_v)| opt_v.map(|v| (k, v))) + } +} + #[derive(Debug, Serialize, Deserialize, uniffi::Record, WPContextual)] pub struct SparsePlugin { #[WPContext(edit, embed, view)] @@ -32,6 +52,15 @@ pub enum PluginStatus { Inactive, } +impl PluginStatus { + fn as_str(&self) -> &str { + match self { + Self::Active => "active", + Self::Inactive => "inactive", + } + } +} + #[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PluginAuthor { pub name: Option, From 5dde634f644ba1b8eaee83207ccceffecaae64ba Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 15 May 2024 14:50:05 -0400 Subject: [PATCH 03/24] Implement list plugins request and its parsers --- wp_api/src/lib.rs | 18 +++++ wp_api/src/plugins.rs | 89 ++++++++++++++++++++++- wp_networking/tests/test_plugins_immut.rs | 49 +++++++++++++ 3 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 wp_networking/tests/test_plugins_immut.rs diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 48644e3db..419a71167 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -7,6 +7,7 @@ pub use api_error::*; pub use endpoint::*; pub use login::*; pub use pages::*; +pub use plugins::*; pub use posts::*; pub use url::*; pub use users::*; @@ -224,6 +225,23 @@ impl WPApiHelper { } } + pub fn list_plugins_request( + &self, + context: WPContext, + params: &Option, // UniFFI doesn't support Option<&T> + ) -> WPNetworkRequest { + WPNetworkRequest { + method: RequestMethod::GET, + url: self + .api_endpoint + .plugins + .list(context, params.as_ref()) + .into(), + header_map: self.header_map(), + body: None, + } + } + fn header_map(&self) -> HashMap { let mut header_map = HashMap::new(); header_map.insert( diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index 53bb37e0c..a5d95764c 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -1,6 +1,40 @@ use serde::{Deserialize, Serialize}; use wp_derive::WPContextual; +use crate::{parse_response_for_generic_errors, WPApiError, WPNetworkResponse}; + +#[uniffi::export] +pub fn parse_list_plugins_response_with_edit_context( + response: &WPNetworkResponse, +) -> Result, WPApiError> { + parse_plugins_response(response) +} + +#[uniffi::export] +pub fn parse_list_plugins_response_with_embed_context( + response: &WPNetworkResponse, +) -> Result, WPApiError> { + parse_plugins_response(response) +} + +#[uniffi::export] +pub fn parse_list_plugins_response_with_view_context( + response: &WPNetworkResponse, +) -> Result, WPApiError> { + parse_plugins_response(response) +} + +// TODO: Duplicate +pub fn parse_plugins_response<'de, T: Deserialize<'de>>( + response: &'de WPNetworkResponse, +) -> Result { + parse_response_for_generic_errors(response)?; + serde_json::from_slice(&response.body).map_err(|err| WPApiError::ParsingError { + reason: err.to_string(), + response: String::from_utf8_lossy(&response.body).to_string(), + }) +} + #[derive(Default, Debug, uniffi::Record)] pub struct PluginListParams { /// Limit results to those matching a string. @@ -33,7 +67,7 @@ pub struct SparsePlugin { // TODO: Custom URI type? pub plugin_uri: Option, #[WPContext(edit, view)] - pub author: Option, + pub author: Option, #[WPContext(edit, view)] pub description: Option, #[WPContext(edit, view)] @@ -48,7 +82,9 @@ pub struct SparsePlugin { #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, uniffi::Enum)] pub enum PluginStatus { + #[serde(rename = "active")] Active, + #[serde(rename = "inactive")] Inactive, } @@ -68,5 +104,54 @@ pub struct PluginAuthor { #[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PluginDescription { - pub name: Option, + pub raw: String, + pub rendered: String, +} + +// TODO: Duplicate +#[macro_export] +macro_rules! plugin_list_params { + () => { + PluginListParams::default() + }; + ($(($f:ident, $v:expr)), *) => {{ + let mut params = PluginListParams::default(); + $(params.$f = $v;)* + params + }}; +} + +#[cfg(test)] +mod tests { + use super::*; + use rstest::*; + + #[rstest] + #[case(plugin_list_params!(), &[])] + #[case(plugin_list_params!((search, Some("foo".to_string()))), &[("search", "foo")])] + #[case(plugin_list_params!((status, Some(PluginStatus::Active))), &[("status", "active")])] + #[case(plugin_list_params!((search, Some("foo".to_string())), (status, Some(PluginStatus::Inactive))), &[("search", "foo"), ("status", "inactive")])] + #[trace] + fn test_plugin_list_params( + #[case] params: PluginListParams, + #[case] expected_pairs: &[(&str, &str)], + ) { + assert_expected_query_pairs(params.query_pairs(), expected_pairs); + } + + // TODO: Duplicate + fn assert_expected_query_pairs<'a>( + query_pairs: impl IntoIterator, + expected_pairs: &[(&'a str, &str)], + ) { + let mut query_pairs = query_pairs.into_iter().collect::>(); + let mut expected_pairs: Vec<(&str, String)> = expected_pairs + .iter() + .map(|(k, v)| (*k, v.to_string())) + .collect(); + // The order of query pairs doesn't matter + query_pairs.sort(); + expected_pairs.sort(); + assert_eq!(query_pairs, expected_pairs); + } } diff --git a/wp_networking/tests/test_plugins_immut.rs b/wp_networking/tests/test_plugins_immut.rs new file mode 100644 index 000000000..37f6c5156 --- /dev/null +++ b/wp_networking/tests/test_plugins_immut.rs @@ -0,0 +1,49 @@ +use rstest::*; +use wp_api::{plugin_list_params, plugins::PluginListParams, plugins::PluginStatus, WPContext}; + +use crate::test_helpers::{api, WPNetworkRequestExecutor}; + +pub mod test_helpers; +#[rstest] +#[case(plugin_list_params!())] +#[case(plugin_list_params!((search, Some("foo".to_string()))))] +#[case(plugin_list_params!((status, Some(PluginStatus::Active))))] +#[case(plugin_list_params!((search, Some("foo".to_string()))))] +#[trace] +#[tokio::test] +async fn test_plugin_list_params_parametrized( + #[case] params: PluginListParams, + #[values(WPContext::Edit, WPContext::Embed, WPContext::View)] context: WPContext, +) { + let response = api() + .list_plugins_request(context, &Some(params)) + .execute() + .await + .unwrap(); + match context { + WPContext::Edit => { + let parsed_response = wp_api::parse_list_plugins_response_with_edit_context(&response); + assert!( + parsed_response.is_ok(), + "Response was: '{:?}'", + parsed_response + ); + } + WPContext::Embed => { + let parsed_response = wp_api::parse_list_plugins_response_with_embed_context(&response); + assert!( + parsed_response.is_ok(), + "Response was: '{:?}'", + parsed_response + ); + } + WPContext::View => { + let parsed_response = wp_api::parse_list_plugins_response_with_view_context(&response); + assert!( + parsed_response.is_ok(), + "Response was: '{:?}'", + parsed_response + ); + } + }; +} From 1cd67886629e31f986952d8de1d24c2d4c2608fe Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 15 May 2024 14:55:47 -0400 Subject: [PATCH 04/24] Replace plugin parsers with add_uniffi_exported_parser --- wp_api/src/plugins.rs | 47 +++++++++++++------------------------------ 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index a5d95764c..4d464021b 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -1,39 +1,20 @@ use serde::{Deserialize, Serialize}; use wp_derive::WPContextual; -use crate::{parse_response_for_generic_errors, WPApiError, WPNetworkResponse}; - -#[uniffi::export] -pub fn parse_list_plugins_response_with_edit_context( - response: &WPNetworkResponse, -) -> Result, WPApiError> { - parse_plugins_response(response) -} - -#[uniffi::export] -pub fn parse_list_plugins_response_with_embed_context( - response: &WPNetworkResponse, -) -> Result, WPApiError> { - parse_plugins_response(response) -} - -#[uniffi::export] -pub fn parse_list_plugins_response_with_view_context( - response: &WPNetworkResponse, -) -> Result, WPApiError> { - parse_plugins_response(response) -} - -// TODO: Duplicate -pub fn parse_plugins_response<'de, T: Deserialize<'de>>( - response: &'de WPNetworkResponse, -) -> Result { - parse_response_for_generic_errors(response)?; - serde_json::from_slice(&response.body).map_err(|err| WPApiError::ParsingError { - reason: err.to_string(), - response: String::from_utf8_lossy(&response.body).to_string(), - }) -} +use crate::{add_uniffi_exported_parser, parse_wp_response, WPApiError, WPNetworkResponse}; + +add_uniffi_exported_parser!( + parse_list_plugins_response_with_edit_context, + Vec +); +add_uniffi_exported_parser!( + parse_list_plugins_response_with_embed_context, + Vec +); +add_uniffi_exported_parser!( + parse_list_plugins_response_with_view_context, + Vec +); #[derive(Default, Debug, uniffi::Record)] pub struct PluginListParams { From c5f60469bc1cbfce8a64f1f8a881c88b1147d583 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 15 May 2024 15:19:20 -0400 Subject: [PATCH 05/24] Replace plugin_list_params! with generate! macro --- wp_api/src/plugins.rs | 22 +++++----------------- wp_networking/tests/test_plugins_immut.rs | 10 +++++----- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index 4d464021b..1da2fdc2c 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -89,29 +89,17 @@ pub struct PluginDescription { pub rendered: String, } -// TODO: Duplicate -#[macro_export] -macro_rules! plugin_list_params { - () => { - PluginListParams::default() - }; - ($(($f:ident, $v:expr)), *) => {{ - let mut params = PluginListParams::default(); - $(params.$f = $v;)* - params - }}; -} - #[cfg(test)] mod tests { use super::*; + use crate::generate; use rstest::*; #[rstest] - #[case(plugin_list_params!(), &[])] - #[case(plugin_list_params!((search, Some("foo".to_string()))), &[("search", "foo")])] - #[case(plugin_list_params!((status, Some(PluginStatus::Active))), &[("status", "active")])] - #[case(plugin_list_params!((search, Some("foo".to_string())), (status, Some(PluginStatus::Inactive))), &[("search", "foo"), ("status", "inactive")])] + #[case(PluginListParams::default(), &[])] + #[case(generate!(PluginListParams, (search, Some("foo".to_string()))), &[("search", "foo")])] + #[case(generate!(PluginListParams, (status, Some(PluginStatus::Active))), &[("status", "active")])] + #[case(generate!(PluginListParams, (search, Some("foo".to_string())), (status, Some(PluginStatus::Inactive))), &[("search", "foo"), ("status", "inactive")])] #[trace] fn test_plugin_list_params( #[case] params: PluginListParams, diff --git a/wp_networking/tests/test_plugins_immut.rs b/wp_networking/tests/test_plugins_immut.rs index 37f6c5156..3399d4252 100644 --- a/wp_networking/tests/test_plugins_immut.rs +++ b/wp_networking/tests/test_plugins_immut.rs @@ -1,14 +1,14 @@ use rstest::*; -use wp_api::{plugin_list_params, plugins::PluginListParams, plugins::PluginStatus, WPContext}; +use wp_api::{modified, plugins::PluginListParams, plugins::PluginStatus, WPContext}; use crate::test_helpers::{api, WPNetworkRequestExecutor}; pub mod test_helpers; #[rstest] -#[case(plugin_list_params!())] -#[case(plugin_list_params!((search, Some("foo".to_string()))))] -#[case(plugin_list_params!((status, Some(PluginStatus::Active))))] -#[case(plugin_list_params!((search, Some("foo".to_string()))))] +#[case(PluginListParams::default())] +#[case(modified!(PluginListParams, (search, Some("foo".to_string()))))] +#[case(modified!(PluginListParams, (status, Some(PluginStatus::Active))))] +#[case(modified!(PluginListParams, (search, Some("foo".to_string()))))] #[trace] #[tokio::test] async fn test_plugin_list_params_parametrized( From b1214fbeb21da5b7e0ac0d616eba996012e9b13d Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 15 May 2024 20:37:05 -0400 Subject: [PATCH 06/24] Use extracted test helpers for plugins --- wp_api/src/endpoint/plugins_endpoint.rs | 25 ++++++++--------------- wp_api/src/plugins.rs | 18 +--------------- wp_networking/tests/test_plugins_immut.rs | 8 ++++---- 3 files changed, 13 insertions(+), 38 deletions(-) diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs index 6361b40a9..6da1d4a38 100644 --- a/wp_api/src/endpoint/plugins_endpoint.rs +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -25,11 +25,15 @@ impl PluginsEndpoint { #[cfg(test)] mod tests { use super::*; - use crate::{plugins::PluginStatus, ApiEndpoint}; + use crate::{ + endpoint::tests::{fixture_api_base_url, validate_endpoint}, + plugins::PluginStatus, + ApiEndpoint, + }; use rstest::*; #[rstest] - fn list_plugins_with_params(api_base_url: ApiBaseUrl, plugins_endpoint: PluginsEndpoint) { + fn list_plugins_with_params(plugins_endpoint: PluginsEndpoint) { let params = PluginListParams { search: Some("foo".to_string()), status: Some(PluginStatus::Active), @@ -37,24 +41,11 @@ mod tests { validate_endpoint( plugins_endpoint.list(WPContext::Edit, Some(¶ms)), "/plugins?context=edit&search=foo&status=active", - &api_base_url, ); } #[fixture] - fn api_base_url() -> ApiBaseUrl { - ApiBaseUrl::new("https://foo.com").unwrap() - } - - #[fixture] - fn plugins_endpoint(api_base_url: ApiBaseUrl) -> PluginsEndpoint { - ApiEndpoint::new(api_base_url).plugins - } - - fn validate_endpoint(endpoint_url: Url, path: &str, api_base_url: &ApiBaseUrl) { - assert_eq!( - endpoint_url.as_str(), - format!("{}{}", api_base_url.as_str(), path) - ); + fn plugins_endpoint(fixture_api_base_url: ApiBaseUrl) -> PluginsEndpoint { + ApiEndpoint::new(fixture_api_base_url).plugins } } diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index 1da2fdc2c..40be64392 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -92,7 +92,7 @@ pub struct PluginDescription { #[cfg(test)] mod tests { use super::*; - use crate::generate; + use crate::{generate, test_helpers::assert_expected_query_pairs}; use rstest::*; #[rstest] @@ -107,20 +107,4 @@ mod tests { ) { assert_expected_query_pairs(params.query_pairs(), expected_pairs); } - - // TODO: Duplicate - fn assert_expected_query_pairs<'a>( - query_pairs: impl IntoIterator, - expected_pairs: &[(&'a str, &str)], - ) { - let mut query_pairs = query_pairs.into_iter().collect::>(); - let mut expected_pairs: Vec<(&str, String)> = expected_pairs - .iter() - .map(|(k, v)| (*k, v.to_string())) - .collect(); - // The order of query pairs doesn't matter - query_pairs.sort(); - expected_pairs.sort(); - assert_eq!(query_pairs, expected_pairs); - } } diff --git a/wp_networking/tests/test_plugins_immut.rs b/wp_networking/tests/test_plugins_immut.rs index 3399d4252..a67b1534b 100644 --- a/wp_networking/tests/test_plugins_immut.rs +++ b/wp_networking/tests/test_plugins_immut.rs @@ -1,14 +1,14 @@ use rstest::*; -use wp_api::{modified, plugins::PluginListParams, plugins::PluginStatus, WPContext}; +use wp_api::{generate, plugins::PluginListParams, plugins::PluginStatus, WPContext}; use crate::test_helpers::{api, WPNetworkRequestExecutor}; pub mod test_helpers; #[rstest] #[case(PluginListParams::default())] -#[case(modified!(PluginListParams, (search, Some("foo".to_string()))))] -#[case(modified!(PluginListParams, (status, Some(PluginStatus::Active))))] -#[case(modified!(PluginListParams, (search, Some("foo".to_string()))))] +#[case(generate!(PluginListParams, (search, Some("foo".to_string()))))] +#[case(generate!(PluginListParams, (status, Some(PluginStatus::Active))))] +#[case(generate!(PluginListParams, (search, Some("foo".to_string()))))] #[trace] #[tokio::test] async fn test_plugin_list_params_parametrized( From fd951bb731f7db36f62022cbc2fa765565611675 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 15 May 2024 21:05:08 -0400 Subject: [PATCH 07/24] Implement create plugin request --- wp_api/src/endpoint/plugins_endpoint.rs | 9 +++++++++ wp_api/src/lib.rs | 9 +++++++++ wp_api/src/plugins.rs | 20 ++++++++++++++++++++ wp_api/src/users.rs | 2 +- wp_networking/tests/test_plugins_mut.rs | 25 +++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 wp_networking/tests/test_plugins_mut.rs diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs index 6da1d4a38..2135f9245 100644 --- a/wp_api/src/endpoint/plugins_endpoint.rs +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -11,6 +11,10 @@ impl PluginsEndpoint { Self { api_base_url } } + pub fn create(&self) -> Url { + self.api_base_url.by_appending("plugins") + } + pub fn list(&self, context: WPContext, params: Option<&PluginListParams>) -> Url { let mut url = self.api_base_url.by_appending("plugins"); url.query_pairs_mut() @@ -32,6 +36,11 @@ mod tests { }; use rstest::*; + #[rstest] + fn create_plugin(plugins_endpoint: PluginsEndpoint) { + validate_endpoint(plugins_endpoint.create(), "/plugins"); + } + #[rstest] fn list_plugins_with_params(plugins_endpoint: PluginsEndpoint) { let params = PluginListParams { diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 419a71167..a1badf086 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -242,6 +242,15 @@ impl WPApiHelper { } } + pub fn create_plugin_request(&self, params: &PluginCreateParams) -> WPNetworkRequest { + WPNetworkRequest { + method: RequestMethod::POST, + url: self.api_endpoint.plugins.create().into(), + header_map: self.header_map_for_post_request(), + body: serde_json::to_vec(¶ms).ok(), + } + } + fn header_map(&self) -> HashMap { let mut header_map = HashMap::new(); header_map.insert( diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index 40be64392..cc8ff76b1 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -15,6 +15,18 @@ add_uniffi_exported_parser!( parse_list_plugins_response_with_view_context, Vec ); +add_uniffi_exported_parser!( + parse_retrieve_plugin_response_with_edit_context, + PluginWithEditContext +); +add_uniffi_exported_parser!( + parse_retrieve_plugin_response_with_embed_context, + PluginWithEmbedContext +); +add_uniffi_exported_parser!( + parse_retrieve_plugin_response_with_view_context, + PluginWithViewContext +); #[derive(Default, Debug, uniffi::Record)] pub struct PluginListParams { @@ -24,6 +36,14 @@ pub struct PluginListParams { pub status: Option, } +#[derive(Serialize, Debug, uniffi::Record)] +pub struct PluginCreateParams { + /// WordPress.org plugin directory slug. + pub slug: String, + /// The plugin activation status. + pub status: PluginStatus, +} + impl PluginListParams { pub fn query_pairs(&self) -> impl IntoIterator { [ diff --git a/wp_api/src/users.rs b/wp_api/src/users.rs index 8b9043511..ac8874498 100644 --- a/wp_api/src/users.rs +++ b/wp_api/src/users.rs @@ -183,7 +183,7 @@ impl UserListParams { } } -#[derive(Serialize, uniffi::Record)] +#[derive(Serialize, Debug, uniffi::Record)] pub struct UserCreateParams { /// Login name for the user. pub username: String, diff --git a/wp_networking/tests/test_plugins_mut.rs b/wp_networking/tests/test_plugins_mut.rs new file mode 100644 index 000000000..d7ee35fba --- /dev/null +++ b/wp_networking/tests/test_plugins_mut.rs @@ -0,0 +1,25 @@ +use wp_api::{PluginCreateParams, PluginStatus}; + +use crate::test_helpers::{api, WPNetworkRequestExecutor, WPNetworkResponseParser}; + +pub mod test_helpers; +pub mod wp_db; + +#[tokio::test] +async fn create_plugin() { + wp_db::run_and_restore(|mut _db| async move { + let slug = "jetpack".to_string(); + let status = PluginStatus::Active; + + let params = PluginCreateParams { slug, status }; + let _created_plugin = api() + .create_plugin_request(¶ms) + .execute() + .await + .unwrap() + .parse(wp_api::parse_retrieve_plugin_response_with_edit_context) + .unwrap(); + println!("{:?}", _created_plugin); + }) + .await; +} From 514ad74adcb27e42e69e4e41df166d82a3310ea3 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 15 May 2024 21:10:19 -0400 Subject: [PATCH 08/24] Implement run_and_restore_wp_content_plugins --- Makefile | 6 +++++ wp_networking/tests/test_helpers.rs | 18 ++++++++++++++- wp_networking/tests/test_plugins_mut.rs | 30 ++++++++++++++----------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 02e9f71c6..1462069cd 100644 --- a/Makefile +++ b/Makefile @@ -201,6 +201,12 @@ dump-mysql: restore-mysql: docker exec -it wordpress-rs-mysql-1 /bin/bash -c "mysql --defaults-extra-file=mysql_config/config.cnf --database wordpress < dump.sql" +backup-wp-content-plugins: + docker exec -it wordpress /bin/bash -c "mkdir -p backup_plugins && cp -a ./wp-content/plugins/. ./backup_plugins" + +restore-wp-content-plugins: + docker exec -it wordpress /bin/bash -c "rm -r ./wp-content/plugins/* && cp -a ./backup_plugins ./wp-content/plugins/." + lint: lint-rust lint-swift lint-rust: diff --git a/wp_networking/tests/test_helpers.rs b/wp_networking/tests/test_helpers.rs index 6a9dd2874..5db6de9c6 100644 --- a/wp_networking/tests/test_helpers.rs +++ b/wp_networking/tests/test_helpers.rs @@ -1,4 +1,5 @@ -use std::fs::read_to_string; +use futures::Future; +use std::{fs::read_to_string, process::Command}; use wp_api::{ UserId, WPApiError, WPApiHelper, WPAuthentication, WPNetworkRequest, WPNetworkResponse, WPRestError, WPRestErrorCode, WPRestErrorWrapper, @@ -147,3 +148,18 @@ fn expected_status_code_for_wp_rest_error_code(error_code: &WPRestErrorCode) -> WPRestErrorCode::UserInvalidUsername => 400, } } + +pub async fn run_and_restore_wp_content_plugins(f: F) +where + F: FnOnce() -> Fut, + Fut: Future, +{ + f().await; + println!("Restoring wp-content/plugins.."); + Command::new("make") + .arg("-C") + .arg("../") + .arg("restore-wp-content-plugins") + .status() + .expect("Failed to restore wp-content/plugins"); +} diff --git a/wp_networking/tests/test_plugins_mut.rs b/wp_networking/tests/test_plugins_mut.rs index d7ee35fba..ac38fcf5b 100644 --- a/wp_networking/tests/test_plugins_mut.rs +++ b/wp_networking/tests/test_plugins_mut.rs @@ -1,25 +1,29 @@ use wp_api::{PluginCreateParams, PluginStatus}; -use crate::test_helpers::{api, WPNetworkRequestExecutor, WPNetworkResponseParser}; +use crate::test_helpers::{ + api, run_and_restore_wp_content_plugins, WPNetworkRequestExecutor, WPNetworkResponseParser, +}; pub mod test_helpers; pub mod wp_db; #[tokio::test] async fn create_plugin() { - wp_db::run_and_restore(|mut _db| async move { - let slug = "jetpack".to_string(); - let status = PluginStatus::Active; + run_and_restore_wp_content_plugins(|| { + wp_db::run_and_restore(|mut _db| async move { + let slug = "jetpack".to_string(); + let status = PluginStatus::Active; - let params = PluginCreateParams { slug, status }; - let _created_plugin = api() - .create_plugin_request(¶ms) - .execute() - .await - .unwrap() - .parse(wp_api::parse_retrieve_plugin_response_with_edit_context) - .unwrap(); - println!("{:?}", _created_plugin); + let params = PluginCreateParams { slug, status }; + let created_plugin = api() + .create_plugin_request(¶ms) + .execute() + .await + .unwrap() + .parse(wp_api::parse_retrieve_plugin_response_with_edit_context) + .unwrap(); + println!("Created Plugin: {:?}", created_plugin); + }) }) .await; } From 8461d813f6fe57e19bec6232e4afc54474df8de5 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 09:35:17 -0400 Subject: [PATCH 09/24] Implements retrieve plugin --- wp_api/src/endpoint/plugins_endpoint.rs | 17 +++++++++++++++++ wp_api/src/lib.rs | 9 +++++++++ wp_networking/tests/test_plugins_immut.rs | 17 ++++++++++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs index 2135f9245..9ab862421 100644 --- a/wp_api/src/endpoint/plugins_endpoint.rs +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -24,6 +24,15 @@ impl PluginsEndpoint { } url } + + pub fn retrieve(&self, plugin: String, context: WPContext) -> Url { + let mut url = self + .api_base_url + .by_extending(["plugins", format!("{}?", plugin).as_str()]); + url.query_pairs_mut() + .append_pair("context", context.as_str()); + url + } } #[cfg(test)] @@ -53,6 +62,14 @@ mod tests { ); } + #[rstest] + fn retrieve_user(plugins_endpoint: PluginsEndpoint) { + validate_endpoint( + plugins_endpoint.retrieve("foo".to_string(), WPContext::View), + "/plugins/foo%3F?context=view", + ); + } + #[fixture] fn plugins_endpoint(fixture_api_base_url: ApiBaseUrl) -> PluginsEndpoint { ApiEndpoint::new(fixture_api_base_url).plugins diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index a1badf086..87133feb8 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -251,6 +251,15 @@ impl WPApiHelper { } } + pub fn retrieve_plugin_request(&self, plugin: String, context: WPContext) -> WPNetworkRequest { + WPNetworkRequest { + method: RequestMethod::GET, + url: self.api_endpoint.plugins.retrieve(plugin, context).into(), + header_map: self.header_map(), + body: None, + } + } + fn header_map(&self) -> HashMap { let mut header_map = HashMap::new(); header_map.insert( diff --git a/wp_networking/tests/test_plugins_immut.rs b/wp_networking/tests/test_plugins_immut.rs index a67b1534b..b7994f8ff 100644 --- a/wp_networking/tests/test_plugins_immut.rs +++ b/wp_networking/tests/test_plugins_immut.rs @@ -1,7 +1,7 @@ use rstest::*; use wp_api::{generate, plugins::PluginListParams, plugins::PluginStatus, WPContext}; -use crate::test_helpers::{api, WPNetworkRequestExecutor}; +use crate::test_helpers::{api, WPNetworkRequestExecutor, WPNetworkResponseParser}; pub mod test_helpers; #[rstest] @@ -47,3 +47,18 @@ async fn test_plugin_list_params_parametrized( } }; } + +#[tokio::test] +async fn retrieve_plugin_with_edit_context() { + let parsed_response = api() + .retrieve_plugin_request("hello".to_string(), WPContext::Edit) + .execute() + .await + .unwrap() + .parse(wp_api::parse_retrieve_plugin_response_with_edit_context); + assert!( + parsed_response.is_ok(), + "Response was: '{:?}'", + parsed_response + ); +} From be2028e1bce1021706b7a6b780ec29a3ccd62d43 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 10:16:48 -0400 Subject: [PATCH 10/24] Fix the retrieve plugin endpoint and its return type --- wp_api/src/endpoint/plugins_endpoint.rs | 19 +++++++++++-------- wp_api/src/plugins.rs | 6 +++--- wp_networking/tests/test_plugins_immut.rs | 2 +- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs index 9ab862421..156556587 100644 --- a/wp_api/src/endpoint/plugins_endpoint.rs +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -12,11 +12,11 @@ impl PluginsEndpoint { } pub fn create(&self) -> Url { - self.api_base_url.by_appending("plugins") + self.plugins_base_url() } pub fn list(&self, context: WPContext, params: Option<&PluginListParams>) -> Url { - let mut url = self.api_base_url.by_appending("plugins"); + let mut url = self.plugins_base_url(); url.query_pairs_mut() .append_pair("context", context.as_str()); if let Some(params) = params { @@ -26,13 +26,16 @@ impl PluginsEndpoint { } pub fn retrieve(&self, plugin: String, context: WPContext) -> Url { - let mut url = self - .api_base_url - .by_extending(["plugins", format!("{}?", plugin).as_str()]); + let mut url = self.plugins_base_url(); + url.query_pairs_mut().append_key_only(plugin.as_str()); url.query_pairs_mut() .append_pair("context", context.as_str()); url } + + fn plugins_base_url(&self) -> Url { + self.api_base_url.by_appending("plugins") + } } #[cfg(test)] @@ -63,10 +66,10 @@ mod tests { } #[rstest] - fn retrieve_user(plugins_endpoint: PluginsEndpoint) { + fn retrieve_plugin(plugins_endpoint: PluginsEndpoint) { validate_endpoint( - plugins_endpoint.retrieve("foo".to_string(), WPContext::View), - "/plugins/foo%3F?context=view", + plugins_endpoint.retrieve("hello-dolly/hello".to_string(), WPContext::View), + "/plugins?hello-dolly%2Fhello&context=view", ); } diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index cc8ff76b1..08d175d1b 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -17,15 +17,15 @@ add_uniffi_exported_parser!( ); add_uniffi_exported_parser!( parse_retrieve_plugin_response_with_edit_context, - PluginWithEditContext + Vec ); add_uniffi_exported_parser!( parse_retrieve_plugin_response_with_embed_context, - PluginWithEmbedContext + Vec ); add_uniffi_exported_parser!( parse_retrieve_plugin_response_with_view_context, - PluginWithViewContext + Vec ); #[derive(Default, Debug, uniffi::Record)] diff --git a/wp_networking/tests/test_plugins_immut.rs b/wp_networking/tests/test_plugins_immut.rs index b7994f8ff..1c4529750 100644 --- a/wp_networking/tests/test_plugins_immut.rs +++ b/wp_networking/tests/test_plugins_immut.rs @@ -51,7 +51,7 @@ async fn test_plugin_list_params_parametrized( #[tokio::test] async fn retrieve_plugin_with_edit_context() { let parsed_response = api() - .retrieve_plugin_request("hello".to_string(), WPContext::Edit) + .retrieve_plugin_request(r"hello-dolly%2Fhello".to_string(), WPContext::Edit) .execute() .await .unwrap() From 887c458ba3fe03c8ae60da2968dce7add73f4dfe Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 10:17:44 -0400 Subject: [PATCH 11/24] A temporary helper bash script for fetching plugins --- fetch-plugin-result.json | 24 ++++++++++++++++++++++++ fetch-plugin.sh | 3 +++ 2 files changed, 27 insertions(+) create mode 100644 fetch-plugin-result.json create mode 100755 fetch-plugin.sh diff --git a/fetch-plugin-result.json b/fetch-plugin-result.json new file mode 100644 index 000000000..c24517bfe --- /dev/null +++ b/fetch-plugin-result.json @@ -0,0 +1,24 @@ +{ + "plugin": "hello-dolly/hello", + "status": "active", + "name": "Hello Dolly", + "plugin_uri": "http://wordpress.org/plugins/hello-dolly/", + "author": "Matt Mullenweg", + "author_uri": "http://ma.tt/", + "description": { + "raw": "This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from Hello, Dolly in the upper right of your admin screen on every page.", + "rendered": "This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from Hello, Dolly in the upper right of your admin screen on every page. By Matt Mullenweg." + }, + "version": "1.7.2", + "network_only": false, + "requires_wp": "", + "requires_php": "", + "textdomain": "hello-dolly", + "_links": { + "self": [ + { + "href": "http://localhost/wp-json/wp/v2/plugins/hello-dolly/hello" + } + ] + } +} diff --git a/fetch-plugin.sh b/fetch-plugin.sh new file mode 100755 index 000000000..9f1b9042e --- /dev/null +++ b/fetch-plugin.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +curl "http://localhost/wp-json/wp/v2/plugins/hello-dolly\/hello?" -u 'test@example.com:xlehGIszv9cA5XgF9rTfCmpY' From 46b9a8a16f1d257592a43ef34cc7bcc6d2899a4b Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 10:18:04 -0400 Subject: [PATCH 12/24] Disable restoring wp-content plugins from plugins_mut tests --- wp_networking/tests/test_helpers.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/wp_networking/tests/test_helpers.rs b/wp_networking/tests/test_helpers.rs index 5db6de9c6..075e89543 100644 --- a/wp_networking/tests/test_helpers.rs +++ b/wp_networking/tests/test_helpers.rs @@ -1,5 +1,5 @@ use futures::Future; -use std::{fs::read_to_string, process::Command}; +use std::fs::read_to_string; use wp_api::{ UserId, WPApiError, WPApiHelper, WPAuthentication, WPNetworkRequest, WPNetworkResponse, WPRestError, WPRestErrorCode, WPRestErrorWrapper, @@ -155,11 +155,12 @@ where Fut: Future, { f().await; - println!("Restoring wp-content/plugins.."); - Command::new("make") - .arg("-C") - .arg("../") - .arg("restore-wp-content-plugins") - .status() - .expect("Failed to restore wp-content/plugins"); + // Temporarily disable the restoration + // println!("Restoring wp-content/plugins.."); + // Command::new("make") + // .arg("-C") + // .arg("../") + // .arg("restore-wp-content-plugins") + // .status() + // .expect("Failed to restore wp-content/plugins"); } From c2bd1c475f7725864225ba3bffd7c13abbdb8f9b Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 10:58:25 -0400 Subject: [PATCH 13/24] Add a separate parser for create plugin response --- create-plugin-response.json | 24 ++++++++++++++++++++++++ create-plugin.sh | 7 +++++++ wp_api/src/plugins.rs | 1 + wp_networking/tests/test_plugins_mut.rs | 2 +- 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 create-plugin-response.json create mode 100755 create-plugin.sh diff --git a/create-plugin-response.json b/create-plugin-response.json new file mode 100644 index 000000000..ecc2b6f47 --- /dev/null +++ b/create-plugin-response.json @@ -0,0 +1,24 @@ +{ + "plugin": "jetpack/jetpack", + "status": "inactive", + "name": "Jetpack", + "plugin_uri": "https://jetpack.com", + "author": "Automattic", + "author_uri": "https://jetpack.com", + "description": { + "raw": "Security, performance, and marketing tools made by WordPress experts. Jetpack keeps your site protected so you can focus on more important things.", + "rendered": "Security, performance, and marketing tools made by WordPress experts. Jetpack keeps your site protected so you can focus on more important things. By Automattic." + }, + "version": "13.4.3", + "network_only": false, + "requires_wp": "6.4", + "requires_php": "7.0", + "textdomain": "jetpack", + "_links": { + "self": [ + { + "href": "http://localhost/wp-json/wp/v2/plugins/jetpack/jetpack" + } + ] + } +} diff --git a/create-plugin.sh b/create-plugin.sh new file mode 100755 index 000000000..9d7ec3c3f --- /dev/null +++ b/create-plugin.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +curl --header "Content-Type: application/json" \ + -u 'test@example.com:xlehGIszv9cA5XgF9rTfCmpY' \ + --request POST \ + --data '{"slug":"jetpack"}' \ + "http://localhost/wp-json/wp/v2/plugins/" diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index 08d175d1b..cbb4ba966 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -27,6 +27,7 @@ add_uniffi_exported_parser!( parse_retrieve_plugin_response_with_view_context, Vec ); +add_uniffi_exported_parser!(parse_create_plugin_response, PluginWithEditContext); #[derive(Default, Debug, uniffi::Record)] pub struct PluginListParams { diff --git a/wp_networking/tests/test_plugins_mut.rs b/wp_networking/tests/test_plugins_mut.rs index ac38fcf5b..e9a7dc976 100644 --- a/wp_networking/tests/test_plugins_mut.rs +++ b/wp_networking/tests/test_plugins_mut.rs @@ -20,7 +20,7 @@ async fn create_plugin() { .execute() .await .unwrap() - .parse(wp_api::parse_retrieve_plugin_response_with_edit_context) + .parse(wp_api::parse_create_plugin_response) .unwrap(); println!("Created Plugin: {:?}", created_plugin); }) From 20af86f2247fcc67a2c279db56a44cb042ba49a1 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 12:27:21 -0400 Subject: [PATCH 14/24] Fix retrieve plugins endpoint --- wp_api/src/endpoint/plugins_endpoint.rs | 12 +++++++----- wp_api/src/lib.rs | 4 ++-- wp_api/src/plugins.rs | 6 +++--- wp_networking/tests/test_plugins_immut.rs | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs index 156556587..4c6bcb454 100644 --- a/wp_api/src/endpoint/plugins_endpoint.rs +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -25,9 +25,11 @@ impl PluginsEndpoint { url } - pub fn retrieve(&self, plugin: String, context: WPContext) -> Url { - let mut url = self.plugins_base_url(); - url.query_pairs_mut().append_key_only(plugin.as_str()); + pub fn retrieve(&self, context: WPContext, plugin: String) -> Url { + let mut url = self + .api_base_url + // The '/' character in `plugin` has to be preserved + .by_extending(["plugins"].into_iter().chain(plugin.split('/'))); url.query_pairs_mut() .append_pair("context", context.as_str()); url @@ -68,8 +70,8 @@ mod tests { #[rstest] fn retrieve_plugin(plugins_endpoint: PluginsEndpoint) { validate_endpoint( - plugins_endpoint.retrieve("hello-dolly/hello".to_string(), WPContext::View), - "/plugins?hello-dolly%2Fhello&context=view", + plugins_endpoint.retrieve(WPContext::View, "hello-dolly/hello".to_string()), + "/plugins/hello-dolly/hello?context=view", ); } diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 87133feb8..14af1bc17 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -251,10 +251,10 @@ impl WPApiHelper { } } - pub fn retrieve_plugin_request(&self, plugin: String, context: WPContext) -> WPNetworkRequest { + pub fn retrieve_plugin_request(&self, context: WPContext, plugin: String) -> WPNetworkRequest { WPNetworkRequest { method: RequestMethod::GET, - url: self.api_endpoint.plugins.retrieve(plugin, context).into(), + url: self.api_endpoint.plugins.retrieve(context, plugin).into(), header_map: self.header_map(), body: None, } diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index cbb4ba966..3acf42d59 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -17,15 +17,15 @@ add_uniffi_exported_parser!( ); add_uniffi_exported_parser!( parse_retrieve_plugin_response_with_edit_context, - Vec + PluginWithEditContext ); add_uniffi_exported_parser!( parse_retrieve_plugin_response_with_embed_context, - Vec + PluginWithEmbedContext ); add_uniffi_exported_parser!( parse_retrieve_plugin_response_with_view_context, - Vec + PluginWithViewContext ); add_uniffi_exported_parser!(parse_create_plugin_response, PluginWithEditContext); diff --git a/wp_networking/tests/test_plugins_immut.rs b/wp_networking/tests/test_plugins_immut.rs index 1c4529750..40ef41236 100644 --- a/wp_networking/tests/test_plugins_immut.rs +++ b/wp_networking/tests/test_plugins_immut.rs @@ -51,7 +51,7 @@ async fn test_plugin_list_params_parametrized( #[tokio::test] async fn retrieve_plugin_with_edit_context() { let parsed_response = api() - .retrieve_plugin_request(r"hello-dolly%2Fhello".to_string(), WPContext::Edit) + .retrieve_plugin_request(WPContext::Edit, "hello-dolly/hello".to_string()) .execute() .await .unwrap() From 71d6f675fe8748b77c419ef2d074e2fa43f64127 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 12:28:31 -0400 Subject: [PATCH 15/24] Implement update plugin request --- update-plugin.sh | 8 ++++++++ wp_api/src/endpoint/plugins_endpoint.rs | 5 +++++ wp_api/src/lib.rs | 14 ++++++++++++++ wp_api/src/plugins.rs | 16 ++++++++-------- 4 files changed, 35 insertions(+), 8 deletions(-) create mode 100755 update-plugin.sh diff --git a/update-plugin.sh b/update-plugin.sh new file mode 100755 index 000000000..fa2ef4d26 --- /dev/null +++ b/update-plugin.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +curl --header "Content-Type: application/json" \ + -u 'test@example.com:xlehGIszv9cA5XgF9rTfCmpY' \ + --request POST \ + --data '{"status":"active"}' \ + "http://localhost/wp-json/wp/v2/plugins/jetpack\/jetpack" + #--data '{"plugin": "jetpack", "status":"inactive"}' \ diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs index 4c6bcb454..3d2cf384f 100644 --- a/wp_api/src/endpoint/plugins_endpoint.rs +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -35,6 +35,11 @@ impl PluginsEndpoint { url } + pub fn update(&self, context: WPContext, plugin: String) -> Url { + // Retrieve & update has the same url (GET vs POST request) + self.retrieve(context, plugin) + } + fn plugins_base_url(&self) -> Url { self.api_base_url.by_appending("plugins") } diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 14af1bc17..585102f4f 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -260,6 +260,20 @@ impl WPApiHelper { } } + pub fn update_plugin_request( + &self, + context: WPContext, + plugin: String, + status: PluginStatus, + ) -> WPNetworkRequest { + WPNetworkRequest { + method: RequestMethod::POST, + url: self.api_endpoint.plugins.update(context, plugin).into(), + header_map: self.header_map_for_post_request(), + body: serde_json::to_vec(&status).ok(), + } + } + fn header_map(&self) -> HashMap { let mut header_map = HashMap::new(); header_map.insert( diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index 3acf42d59..74adc1797 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -37,14 +37,6 @@ pub struct PluginListParams { pub status: Option, } -#[derive(Serialize, Debug, uniffi::Record)] -pub struct PluginCreateParams { - /// WordPress.org plugin directory slug. - pub slug: String, - /// The plugin activation status. - pub status: PluginStatus, -} - impl PluginListParams { pub fn query_pairs(&self) -> impl IntoIterator { [ @@ -57,6 +49,14 @@ impl PluginListParams { } } +#[derive(Serialize, Debug, uniffi::Record)] +pub struct PluginCreateParams { + /// WordPress.org plugin directory slug. + pub slug: String, + /// The plugin activation status. + pub status: PluginStatus, +} + #[derive(Debug, Serialize, Deserialize, uniffi::Record, WPContextual)] pub struct SparsePlugin { #[WPContext(edit, embed, view)] From 6f51a7390dfb5bda7076b62f4d6a0f1215cba834 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 12:52:38 -0400 Subject: [PATCH 16/24] Add an integration test for update plugin --- update-plugin.sh | 4 ++-- wp_api/src/endpoint/plugins_endpoint.rs | 16 +++++++++------- wp_api/src/lib.rs | 7 +++---- wp_api/src/plugins.rs | 13 ++++++++++++- wp_networking/tests/test_plugins_mut.rs | 22 ++++++++++++++++++++-- 5 files changed, 46 insertions(+), 16 deletions(-) diff --git a/update-plugin.sh b/update-plugin.sh index fa2ef4d26..aac941499 100755 --- a/update-plugin.sh +++ b/update-plugin.sh @@ -3,6 +3,6 @@ curl --header "Content-Type: application/json" \ -u 'test@example.com:xlehGIszv9cA5XgF9rTfCmpY' \ --request POST \ - --data '{"status":"active"}' \ - "http://localhost/wp-json/wp/v2/plugins/jetpack\/jetpack" + --data '{"status":"active", "context": "view"}' \ + "http://localhost/wp-json/wp/v2/plugins/hello-dolly/hello?context=view" #--data '{"plugin": "jetpack", "status":"inactive"}' \ diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs index 3d2cf384f..b7e88ee6a 100644 --- a/wp_api/src/endpoint/plugins_endpoint.rs +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -26,23 +26,25 @@ impl PluginsEndpoint { } pub fn retrieve(&self, context: WPContext, plugin: String) -> Url { - let mut url = self - .api_base_url - // The '/' character in `plugin` has to be preserved - .by_extending(["plugins"].into_iter().chain(plugin.split('/'))); + let mut url = self.plugins_url_with_slug(plugin); url.query_pairs_mut() .append_pair("context", context.as_str()); url } - pub fn update(&self, context: WPContext, plugin: String) -> Url { - // Retrieve & update has the same url (GET vs POST request) - self.retrieve(context, plugin) + pub fn update(&self, plugin: String) -> Url { + self.plugins_url_with_slug(plugin) } fn plugins_base_url(&self) -> Url { self.api_base_url.by_appending("plugins") } + + fn plugins_url_with_slug(&self, plugin: String) -> Url { + self.api_base_url + // The '/' character has to be preserved and not get encoded + .by_extending(["plugins"].into_iter().chain(plugin.split('/'))) + } } #[cfg(test)] diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 585102f4f..263f548f7 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -262,15 +262,14 @@ impl WPApiHelper { pub fn update_plugin_request( &self, - context: WPContext, plugin: String, - status: PluginStatus, + params: PluginUpdateParams, ) -> WPNetworkRequest { WPNetworkRequest { method: RequestMethod::POST, - url: self.api_endpoint.plugins.update(context, plugin).into(), + url: self.api_endpoint.plugins.update(plugin).into(), header_map: self.header_map_for_post_request(), - body: serde_json::to_vec(&status).ok(), + body: serde_json::to_vec(¶ms).ok(), } } diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index 74adc1797..1fca3709e 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -28,6 +28,7 @@ add_uniffi_exported_parser!( PluginWithViewContext ); add_uniffi_exported_parser!(parse_create_plugin_response, PluginWithEditContext); +add_uniffi_exported_parser!(parse_update_plugin_response, PluginWithEditContext); #[derive(Default, Debug, uniffi::Record)] pub struct PluginListParams { @@ -49,7 +50,7 @@ impl PluginListParams { } } -#[derive(Serialize, Debug, uniffi::Record)] +#[derive(Debug, Serialize, uniffi::Record)] pub struct PluginCreateParams { /// WordPress.org plugin directory slug. pub slug: String, @@ -57,6 +58,16 @@ pub struct PluginCreateParams { pub status: PluginStatus, } +#[derive(Debug, Serialize, uniffi::Record)] +pub struct PluginUpdateParams { + /// The plugin activation status. + pub status: PluginStatus, + // According to the documentation: https://developer.wordpress.org/rest-api/reference/plugins/#update-a-plugin + // There is supposed to be a `context` parameter as well, but this parameter doesn't seem to + // modify the response fields as promised in the documentation. + // In order to avoid confusion, this parameter is not included in this implementation. +} + #[derive(Debug, Serialize, Deserialize, uniffi::Record, WPContextual)] pub struct SparsePlugin { #[WPContext(edit, embed, view)] diff --git a/wp_networking/tests/test_plugins_mut.rs b/wp_networking/tests/test_plugins_mut.rs index e9a7dc976..26905b7a1 100644 --- a/wp_networking/tests/test_plugins_mut.rs +++ b/wp_networking/tests/test_plugins_mut.rs @@ -1,4 +1,4 @@ -use wp_api::{PluginCreateParams, PluginStatus}; +use wp_api::{PluginCreateParams, PluginStatus, PluginUpdateParams}; use crate::test_helpers::{ api, run_and_restore_wp_content_plugins, WPNetworkRequestExecutor, WPNetworkResponseParser, @@ -13,7 +13,6 @@ async fn create_plugin() { wp_db::run_and_restore(|mut _db| async move { let slug = "jetpack".to_string(); let status = PluginStatus::Active; - let params = PluginCreateParams { slug, status }; let created_plugin = api() .create_plugin_request(¶ms) @@ -27,3 +26,22 @@ async fn create_plugin() { }) .await; } + +#[tokio::test] +async fn update_plugin() { + run_and_restore_wp_content_plugins(|| { + wp_db::run_and_restore(|mut _db| async move { + let slug = "hello-dolly/hello".to_string(); + let status = PluginStatus::Active; + let updated_plugin = api() + .update_plugin_request(slug, PluginUpdateParams { status }) + .execute() + .await + .unwrap() + .parse(wp_api::parse_update_plugin_response) + .unwrap(); + println!("Updated Plugin: {:?}", updated_plugin); + }) + }) + .await; +} From 259d0095f0836fbd3e7607cdd092f49a303d6356 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 13:08:18 -0400 Subject: [PATCH 17/24] Implement delete plugin request --- wp_api/src/endpoint/plugins_endpoint.rs | 28 +++++++++++++++++++---- wp_api/src/lib.rs | 13 +++++++++-- wp_api/src/plugins.rs | 7 ++++++ wp_networking/tests/test_helpers.rs | 1 + wp_networking/tests/test_plugins_immut.rs | 2 +- wp_networking/tests/test_plugins_mut.rs | 27 ++++++++++++++++++---- 6 files changed, 67 insertions(+), 11 deletions(-) diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs index b7e88ee6a..948479be9 100644 --- a/wp_api/src/endpoint/plugins_endpoint.rs +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -15,6 +15,10 @@ impl PluginsEndpoint { self.plugins_base_url() } + pub fn delete(&self, plugin: &str) -> Url { + self.plugins_url_with_slug(plugin) + } + pub fn list(&self, context: WPContext, params: Option<&PluginListParams>) -> Url { let mut url = self.plugins_base_url(); url.query_pairs_mut() @@ -25,14 +29,14 @@ impl PluginsEndpoint { url } - pub fn retrieve(&self, context: WPContext, plugin: String) -> Url { + pub fn retrieve(&self, context: WPContext, plugin: &str) -> Url { let mut url = self.plugins_url_with_slug(plugin); url.query_pairs_mut() .append_pair("context", context.as_str()); url } - pub fn update(&self, plugin: String) -> Url { + pub fn update(&self, plugin: &str) -> Url { self.plugins_url_with_slug(plugin) } @@ -40,7 +44,7 @@ impl PluginsEndpoint { self.api_base_url.by_appending("plugins") } - fn plugins_url_with_slug(&self, plugin: String) -> Url { + fn plugins_url_with_slug(&self, plugin: &str) -> Url { self.api_base_url // The '/' character has to be preserved and not get encoded .by_extending(["plugins"].into_iter().chain(plugin.split('/'))) @@ -62,6 +66,14 @@ mod tests { validate_endpoint(plugins_endpoint.create(), "/plugins"); } + #[rstest] + fn delete_plugin(plugins_endpoint: PluginsEndpoint) { + validate_endpoint( + plugins_endpoint.delete("hello-dolly/hello"), + "/plugins/hello-dolly/hello", + ); + } + #[rstest] fn list_plugins_with_params(plugins_endpoint: PluginsEndpoint) { let params = PluginListParams { @@ -77,11 +89,19 @@ mod tests { #[rstest] fn retrieve_plugin(plugins_endpoint: PluginsEndpoint) { validate_endpoint( - plugins_endpoint.retrieve(WPContext::View, "hello-dolly/hello".to_string()), + plugins_endpoint.retrieve(WPContext::View, "hello-dolly/hello"), "/plugins/hello-dolly/hello?context=view", ); } + #[rstest] + fn update_plugin(plugins_endpoint: PluginsEndpoint) { + validate_endpoint( + plugins_endpoint.update("hello-dolly/hello"), + "/plugins/hello-dolly/hello", + ); + } + #[fixture] fn plugins_endpoint(fixture_api_base_url: ApiBaseUrl) -> PluginsEndpoint { ApiEndpoint::new(fixture_api_base_url).plugins diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 263f548f7..f1a9e8734 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -251,7 +251,7 @@ impl WPApiHelper { } } - pub fn retrieve_plugin_request(&self, context: WPContext, plugin: String) -> WPNetworkRequest { + pub fn retrieve_plugin_request(&self, context: WPContext, plugin: &str) -> WPNetworkRequest { WPNetworkRequest { method: RequestMethod::GET, url: self.api_endpoint.plugins.retrieve(context, plugin).into(), @@ -262,7 +262,7 @@ impl WPApiHelper { pub fn update_plugin_request( &self, - plugin: String, + plugin: &str, params: PluginUpdateParams, ) -> WPNetworkRequest { WPNetworkRequest { @@ -273,6 +273,15 @@ impl WPApiHelper { } } + pub fn delete_plugin_request(&self, plugin: &str) -> WPNetworkRequest { + WPNetworkRequest { + method: RequestMethod::DELETE, + url: self.api_endpoint.plugins.delete(plugin).into(), + header_map: self.header_map(), + body: None, + } + } + fn header_map(&self) -> HashMap { let mut header_map = HashMap::new(); header_map.insert( diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index 1fca3709e..a47f43af3 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -29,6 +29,7 @@ add_uniffi_exported_parser!( ); add_uniffi_exported_parser!(parse_create_plugin_response, PluginWithEditContext); add_uniffi_exported_parser!(parse_update_plugin_response, PluginWithEditContext); +add_uniffi_exported_parser!(parse_delete_plugin_response, PluginDeleteResponse); #[derive(Default, Debug, uniffi::Record)] pub struct PluginListParams { @@ -93,6 +94,12 @@ pub struct SparsePlugin { pub textdomain: Option, } +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] +pub struct PluginDeleteResponse { + pub deleted: bool, + pub previous: PluginWithEditContext, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, uniffi::Enum)] pub enum PluginStatus { #[serde(rename = "active")] diff --git a/wp_networking/tests/test_helpers.rs b/wp_networking/tests/test_helpers.rs index 075e89543..c5722ace2 100644 --- a/wp_networking/tests/test_helpers.rs +++ b/wp_networking/tests/test_helpers.rs @@ -12,6 +12,7 @@ pub const FIRST_USER_ID: UserId = UserId(1); pub const SECOND_USER_ID: UserId = UserId(2); pub const SECOND_USER_EMAIL: &str = "themeshaperwp+demos@gmail.com"; pub const SECOND_USER_SLUG: &str = "themedemos"; +pub const HELLO_DOLLY_PLUGIN_SLUG: &str = "hello-dolly/hello"; pub fn api() -> WPApiHelper { let credentials = read_test_credentials_from_file(); diff --git a/wp_networking/tests/test_plugins_immut.rs b/wp_networking/tests/test_plugins_immut.rs index 40ef41236..252a6b6b4 100644 --- a/wp_networking/tests/test_plugins_immut.rs +++ b/wp_networking/tests/test_plugins_immut.rs @@ -51,7 +51,7 @@ async fn test_plugin_list_params_parametrized( #[tokio::test] async fn retrieve_plugin_with_edit_context() { let parsed_response = api() - .retrieve_plugin_request(WPContext::Edit, "hello-dolly/hello".to_string()) + .retrieve_plugin_request(WPContext::Edit, "hello-dolly/hello") .execute() .await .unwrap() diff --git a/wp_networking/tests/test_plugins_mut.rs b/wp_networking/tests/test_plugins_mut.rs index 26905b7a1..f285c4ce2 100644 --- a/wp_networking/tests/test_plugins_mut.rs +++ b/wp_networking/tests/test_plugins_mut.rs @@ -2,6 +2,7 @@ use wp_api::{PluginCreateParams, PluginStatus, PluginUpdateParams}; use crate::test_helpers::{ api, run_and_restore_wp_content_plugins, WPNetworkRequestExecutor, WPNetworkResponseParser, + HELLO_DOLLY_PLUGIN_SLUG, }; pub mod test_helpers; @@ -11,9 +12,11 @@ pub mod wp_db; async fn create_plugin() { run_and_restore_wp_content_plugins(|| { wp_db::run_and_restore(|mut _db| async move { - let slug = "jetpack".to_string(); let status = PluginStatus::Active; - let params = PluginCreateParams { slug, status }; + let params = PluginCreateParams { + slug: HELLO_DOLLY_PLUGIN_SLUG.to_string(), + status, + }; let created_plugin = api() .create_plugin_request(¶ms) .execute() @@ -31,10 +34,9 @@ async fn create_plugin() { async fn update_plugin() { run_and_restore_wp_content_plugins(|| { wp_db::run_and_restore(|mut _db| async move { - let slug = "hello-dolly/hello".to_string(); let status = PluginStatus::Active; let updated_plugin = api() - .update_plugin_request(slug, PluginUpdateParams { status }) + .update_plugin_request(HELLO_DOLLY_PLUGIN_SLUG, PluginUpdateParams { status }) .execute() .await .unwrap() @@ -45,3 +47,20 @@ async fn update_plugin() { }) .await; } + +#[tokio::test] +async fn delete_plugin() { + run_and_restore_wp_content_plugins(|| { + wp_db::run_and_restore(|mut _db| async move { + let deleted_plugin = api() + .delete_plugin_request(HELLO_DOLLY_PLUGIN_SLUG) + .execute() + .await + .unwrap() + .parse(wp_api::parse_delete_plugin_response) + .unwrap(); + println!("Deleted Plugin: {:?}", deleted_plugin); + }) + }) + .await; +} From 817a08849dc90012fc9fa4144a8524135e09c43e Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 13:33:48 -0400 Subject: [PATCH 18/24] Improve unit & immut integration tests for plugins --- fetch-plugin.sh | 1 + wp_api/src/endpoint/plugins_endpoint.rs | 75 +++++++++++++++-------- wp_api/src/plugins.rs | 1 - wp_networking/tests/test_helpers.rs | 1 + wp_networking/tests/test_plugins_immut.rs | 22 +++++-- 5 files changed, 70 insertions(+), 30 deletions(-) diff --git a/fetch-plugin.sh b/fetch-plugin.sh index 9f1b9042e..eab31092c 100755 --- a/fetch-plugin.sh +++ b/fetch-plugin.sh @@ -1,3 +1,4 @@ #!/bin/bash curl "http://localhost/wp-json/wp/v2/plugins/hello-dolly\/hello?" -u 'test@example.com:xlehGIszv9cA5XgF9rTfCmpY' +curl "http://localhost/wp-json/wp/v2/plugins/classic-editor/classic-editor?" -u 'test@example.com:xlehGIszv9cA5XgF9rTfCmpY' diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs index 948479be9..0f895f043 100644 --- a/wp_api/src/endpoint/plugins_endpoint.rs +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -56,8 +56,7 @@ mod tests { use super::*; use crate::{ endpoint::tests::{fixture_api_base_url, validate_endpoint}, - plugins::PluginStatus, - ApiEndpoint, + generate, ApiEndpoint, PluginStatus, }; use rstest::*; @@ -67,39 +66,67 @@ mod tests { } #[rstest] - fn delete_plugin(plugins_endpoint: PluginsEndpoint) { - validate_endpoint( - plugins_endpoint.delete("hello-dolly/hello"), - "/plugins/hello-dolly/hello", - ); + #[case("hello-dolly/hello", "/plugins/hello-dolly/hello")] + #[case( + "classic-editor/classic-editor", + "/plugins/classic-editor/classic-editor" + )] + fn delete_plugin( + plugins_endpoint: PluginsEndpoint, + #[case] plugin_slug: &str, + #[case] expected_path: &str, + ) { + validate_endpoint(plugins_endpoint.delete(plugin_slug), expected_path); } #[rstest] - fn list_plugins_with_params(plugins_endpoint: PluginsEndpoint) { - let params = PluginListParams { - search: Some("foo".to_string()), - status: Some(PluginStatus::Active), - }; - validate_endpoint( - plugins_endpoint.list(WPContext::Edit, Some(¶ms)), - "/plugins?context=edit&search=foo&status=active", - ); + #[case(WPContext::Edit, generate!(PluginListParams, (search, Some("foo".to_string()))), "/plugins?context=edit&search=foo")] + #[case(WPContext::Embed, generate!(PluginListParams, (status, Some(PluginStatus::Active))), "/plugins?context=embed&status=active")] + #[case(WPContext::View, generate!(PluginListParams, (search, Some("foo".to_string())), (status, Some(PluginStatus::Inactive))), "/plugins?context=view&search=foo&status=inactive")] + fn list_plugins_with_params( + plugins_endpoint: PluginsEndpoint, + #[case] context: WPContext, + #[case] params: PluginListParams, + #[case] expected_path: &str, + ) { + validate_endpoint(plugins_endpoint.list(context, Some(¶ms)), expected_path); } #[rstest] - fn retrieve_plugin(plugins_endpoint: PluginsEndpoint) { + #[case( + "hello-dolly/hello", + WPContext::View, + "/plugins/hello-dolly/hello?context=view" + )] + #[case( + "classic-editor/classic-editor", + WPContext::Embed, + "/plugins/classic-editor/classic-editor?context=embed" + )] + fn retrieve_plugin( + plugins_endpoint: PluginsEndpoint, + #[case] plugin_slug: &str, + #[case] context: WPContext, + #[case] expected_path: &str, + ) { validate_endpoint( - plugins_endpoint.retrieve(WPContext::View, "hello-dolly/hello"), - "/plugins/hello-dolly/hello?context=view", + plugins_endpoint.retrieve(context, plugin_slug), + expected_path, ); } #[rstest] - fn update_plugin(plugins_endpoint: PluginsEndpoint) { - validate_endpoint( - plugins_endpoint.update("hello-dolly/hello"), - "/plugins/hello-dolly/hello", - ); + #[case("hello-dolly/hello", "/plugins/hello-dolly/hello")] + #[case( + "classic-editor/classic-editor", + "/plugins/classic-editor/classic-editor" + )] + fn update_plugin( + plugins_endpoint: PluginsEndpoint, + #[case] plugin_slug: &str, + #[case] expected_path: &str, + ) { + validate_endpoint(plugins_endpoint.update(plugin_slug), expected_path); } #[fixture] diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index a47f43af3..82619ea61 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -78,7 +78,6 @@ pub struct SparsePlugin { #[WPContext(edit, embed, view)] pub name: Option, #[WPContext(edit, view)] - // TODO: Custom URI type? pub plugin_uri: Option, #[WPContext(edit, view)] pub author: Option, diff --git a/wp_networking/tests/test_helpers.rs b/wp_networking/tests/test_helpers.rs index c5722ace2..b8edc31d8 100644 --- a/wp_networking/tests/test_helpers.rs +++ b/wp_networking/tests/test_helpers.rs @@ -13,6 +13,7 @@ pub const SECOND_USER_ID: UserId = UserId(2); pub const SECOND_USER_EMAIL: &str = "themeshaperwp+demos@gmail.com"; pub const SECOND_USER_SLUG: &str = "themedemos"; pub const HELLO_DOLLY_PLUGIN_SLUG: &str = "hello-dolly/hello"; +pub const CLASSIC_EDITOR_PLUGIN_SLUG: &str = "classic-editor/classic-editor"; pub fn api() -> WPApiHelper { let credentials = read_test_credentials_from_file(); diff --git a/wp_networking/tests/test_plugins_immut.rs b/wp_networking/tests/test_plugins_immut.rs index 252a6b6b4..5a143132e 100644 --- a/wp_networking/tests/test_plugins_immut.rs +++ b/wp_networking/tests/test_plugins_immut.rs @@ -1,14 +1,17 @@ use rstest::*; use wp_api::{generate, plugins::PluginListParams, plugins::PluginStatus, WPContext}; -use crate::test_helpers::{api, WPNetworkRequestExecutor, WPNetworkResponseParser}; +use crate::test_helpers::{ + api, WPNetworkRequestExecutor, WPNetworkResponseParser, CLASSIC_EDITOR_PLUGIN_SLUG, + HELLO_DOLLY_PLUGIN_SLUG, +}; pub mod test_helpers; #[rstest] #[case(PluginListParams::default())] #[case(generate!(PluginListParams, (search, Some("foo".to_string()))))] #[case(generate!(PluginListParams, (status, Some(PluginStatus::Active))))] -#[case(generate!(PluginListParams, (search, Some("foo".to_string()))))] +#[case(generate!(PluginListParams, (search, Some("foo".to_string())), (status, Some(PluginStatus::Inactive))))] #[trace] #[tokio::test] async fn test_plugin_list_params_parametrized( @@ -48,17 +51,26 @@ async fn test_plugin_list_params_parametrized( }; } +#[rstest] +#[case(CLASSIC_EDITOR_PLUGIN_SLUG)] +#[case(HELLO_DOLLY_PLUGIN_SLUG)] +#[trace] #[tokio::test] -async fn retrieve_plugin_with_edit_context() { +async fn retrieve_plugin_with_edit_context( + #[case] plugin_slug: &str, + #[values(WPContext::Edit, WPContext::Embed, WPContext::View)] context: WPContext, +) { let parsed_response = api() - .retrieve_plugin_request(WPContext::Edit, "hello-dolly/hello") + .retrieve_plugin_request(context, plugin_slug) .execute() .await .unwrap() .parse(wp_api::parse_retrieve_plugin_response_with_edit_context); assert!( parsed_response.is_ok(), - "Response was: '{:?}'", + "Retrievew plugin failed!\nContext: {:?}\nPlugin: {}\nResponse was: '{:?}'", + context, + plugin_slug, parsed_response ); } From 2afc5af5504f5d2394d9fcf78e4879f3e2398701 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 13:52:11 -0400 Subject: [PATCH 19/24] Fix backup/restoration of wp-content/plugins for integration tests --- .buildkite/setup-test-site.sh | 4 ++++ Makefile | 9 ++++++--- create-plugin.sh | 4 ++-- wp_networking/tests/test_helpers.rs | 18 +++++++++--------- wp_networking/tests/test_plugins_mut.rs | 6 +++--- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/.buildkite/setup-test-site.sh b/.buildkite/setup-test-site.sh index d2cb32c68..fbcb0e926 100755 --- a/.buildkite/setup-test-site.sh +++ b/.buildkite/setup-test-site.sh @@ -42,3 +42,7 @@ wp plugin delete wordpress-importer ## Create an Application password for a subscriber user, and store it where it can be used by the test suite wp user application-password create themedemos test --porcelain } >> /tmp/test_credentials + +## Used for integration tests +wp plugin install hello-dolly --activate +wp plugin install classic-editor diff --git a/Makefile b/Makefile index 1462069cd..1c625a309 100644 --- a/Makefile +++ b/Makefile @@ -192,7 +192,7 @@ test-server: stop-server docker-compose up -d docker-compose run wpcli -stop-server: +stop-server: delete-wp-plugins-backup docker-compose down dump-mysql: @@ -202,10 +202,13 @@ restore-mysql: docker exec -it wordpress-rs-mysql-1 /bin/bash -c "mysql --defaults-extra-file=mysql_config/config.cnf --database wordpress < dump.sql" backup-wp-content-plugins: - docker exec -it wordpress /bin/bash -c "mkdir -p backup_plugins && cp -a ./wp-content/plugins/. ./backup_plugins" + docker exec -it wordpress /bin/bash -c "cp -R ./wp-content/plugins /tmp/backup_wp_plugins" restore-wp-content-plugins: - docker exec -it wordpress /bin/bash -c "rm -r ./wp-content/plugins/* && cp -a ./backup_plugins ./wp-content/plugins/." + docker exec -it wordpress /bin/bash -c "rm -rf ./wp-content/plugins && cp -R /tmp/backup_wp_plugins ./wp-content/plugins" + +delete-wp-plugins-backup: + docker exec -it wordpress /bin/bash -c "rm -rf /tmp/backup_wp_plugins" lint: lint-rust lint-swift diff --git a/create-plugin.sh b/create-plugin.sh index 9d7ec3c3f..cdfed8574 100755 --- a/create-plugin.sh +++ b/create-plugin.sh @@ -1,7 +1,7 @@ #!/bin/bash curl --header "Content-Type: application/json" \ - -u 'test@example.com:xlehGIszv9cA5XgF9rTfCmpY' \ + -u 'test@example.com:ZluvFUYwYOFs5o3u3Th4u3gy' \ --request POST \ - --data '{"slug":"jetpack"}' \ + --data '{"slug":"hello-dolly"}' \ "http://localhost/wp-json/wp/v2/plugins/" diff --git a/wp_networking/tests/test_helpers.rs b/wp_networking/tests/test_helpers.rs index b8edc31d8..b2f9847b8 100644 --- a/wp_networking/tests/test_helpers.rs +++ b/wp_networking/tests/test_helpers.rs @@ -1,5 +1,5 @@ use futures::Future; -use std::fs::read_to_string; +use std::{fs::read_to_string, process::Command}; use wp_api::{ UserId, WPApiError, WPApiHelper, WPAuthentication, WPNetworkRequest, WPNetworkResponse, WPRestError, WPRestErrorCode, WPRestErrorWrapper, @@ -14,6 +14,7 @@ pub const SECOND_USER_EMAIL: &str = "themeshaperwp+demos@gmail.com"; pub const SECOND_USER_SLUG: &str = "themedemos"; pub const HELLO_DOLLY_PLUGIN_SLUG: &str = "hello-dolly/hello"; pub const CLASSIC_EDITOR_PLUGIN_SLUG: &str = "classic-editor/classic-editor"; +pub const WP_ORG_PLUGIN_SLUG_CLASSIC_WIDGETS: &str = "classic-widgets"; pub fn api() -> WPApiHelper { let credentials = read_test_credentials_from_file(); @@ -157,12 +158,11 @@ where Fut: Future, { f().await; - // Temporarily disable the restoration - // println!("Restoring wp-content/plugins.."); - // Command::new("make") - // .arg("-C") - // .arg("../") - // .arg("restore-wp-content-plugins") - // .status() - // .expect("Failed to restore wp-content/plugins"); + println!("Restoring wp-content/plugins.."); + Command::new("make") + .arg("-C") + .arg("../") + .arg("restore-wp-content-plugins") + .status() + .expect("Failed to restore wp-content/plugins"); } diff --git a/wp_networking/tests/test_plugins_mut.rs b/wp_networking/tests/test_plugins_mut.rs index f285c4ce2..7a3455835 100644 --- a/wp_networking/tests/test_plugins_mut.rs +++ b/wp_networking/tests/test_plugins_mut.rs @@ -2,7 +2,7 @@ use wp_api::{PluginCreateParams, PluginStatus, PluginUpdateParams}; use crate::test_helpers::{ api, run_and_restore_wp_content_plugins, WPNetworkRequestExecutor, WPNetworkResponseParser, - HELLO_DOLLY_PLUGIN_SLUG, + CLASSIC_EDITOR_PLUGIN_SLUG, HELLO_DOLLY_PLUGIN_SLUG, WP_ORG_PLUGIN_SLUG_CLASSIC_WIDGETS, }; pub mod test_helpers; @@ -14,7 +14,7 @@ async fn create_plugin() { wp_db::run_and_restore(|mut _db| async move { let status = PluginStatus::Active; let params = PluginCreateParams { - slug: HELLO_DOLLY_PLUGIN_SLUG.to_string(), + slug: WP_ORG_PLUGIN_SLUG_CLASSIC_WIDGETS.to_string(), status, }; let created_plugin = api() @@ -53,7 +53,7 @@ async fn delete_plugin() { run_and_restore_wp_content_plugins(|| { wp_db::run_and_restore(|mut _db| async move { let deleted_plugin = api() - .delete_plugin_request(HELLO_DOLLY_PLUGIN_SLUG) + .delete_plugin_request(CLASSIC_EDITOR_PLUGIN_SLUG) .execute() .await .unwrap() From ef837fb06151104ef350a3b190e762ea14a1fb12 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 14:16:12 -0400 Subject: [PATCH 20/24] Remove temporary scripts used to test plugins --- create-plugin-response.json | 24 ------------------------ create-plugin.sh | 7 ------- fetch-plugin-result.json | 24 ------------------------ fetch-plugin.sh | 4 ---- update-plugin.sh | 8 -------- 5 files changed, 67 deletions(-) delete mode 100644 create-plugin-response.json delete mode 100755 create-plugin.sh delete mode 100644 fetch-plugin-result.json delete mode 100755 fetch-plugin.sh delete mode 100755 update-plugin.sh diff --git a/create-plugin-response.json b/create-plugin-response.json deleted file mode 100644 index ecc2b6f47..000000000 --- a/create-plugin-response.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "plugin": "jetpack/jetpack", - "status": "inactive", - "name": "Jetpack", - "plugin_uri": "https://jetpack.com", - "author": "Automattic", - "author_uri": "https://jetpack.com", - "description": { - "raw": "Security, performance, and marketing tools made by WordPress experts. Jetpack keeps your site protected so you can focus on more important things.", - "rendered": "Security, performance, and marketing tools made by WordPress experts. Jetpack keeps your site protected so you can focus on more important things. By Automattic." - }, - "version": "13.4.3", - "network_only": false, - "requires_wp": "6.4", - "requires_php": "7.0", - "textdomain": "jetpack", - "_links": { - "self": [ - { - "href": "http://localhost/wp-json/wp/v2/plugins/jetpack/jetpack" - } - ] - } -} diff --git a/create-plugin.sh b/create-plugin.sh deleted file mode 100755 index cdfed8574..000000000 --- a/create-plugin.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -curl --header "Content-Type: application/json" \ - -u 'test@example.com:ZluvFUYwYOFs5o3u3Th4u3gy' \ - --request POST \ - --data '{"slug":"hello-dolly"}' \ - "http://localhost/wp-json/wp/v2/plugins/" diff --git a/fetch-plugin-result.json b/fetch-plugin-result.json deleted file mode 100644 index c24517bfe..000000000 --- a/fetch-plugin-result.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "plugin": "hello-dolly/hello", - "status": "active", - "name": "Hello Dolly", - "plugin_uri": "http://wordpress.org/plugins/hello-dolly/", - "author": "Matt Mullenweg", - "author_uri": "http://ma.tt/", - "description": { - "raw": "This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from Hello, Dolly in the upper right of your admin screen on every page.", - "rendered": "This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from Hello, Dolly in the upper right of your admin screen on every page. By Matt Mullenweg." - }, - "version": "1.7.2", - "network_only": false, - "requires_wp": "", - "requires_php": "", - "textdomain": "hello-dolly", - "_links": { - "self": [ - { - "href": "http://localhost/wp-json/wp/v2/plugins/hello-dolly/hello" - } - ] - } -} diff --git a/fetch-plugin.sh b/fetch-plugin.sh deleted file mode 100755 index eab31092c..000000000 --- a/fetch-plugin.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -curl "http://localhost/wp-json/wp/v2/plugins/hello-dolly\/hello?" -u 'test@example.com:xlehGIszv9cA5XgF9rTfCmpY' -curl "http://localhost/wp-json/wp/v2/plugins/classic-editor/classic-editor?" -u 'test@example.com:xlehGIszv9cA5XgF9rTfCmpY' diff --git a/update-plugin.sh b/update-plugin.sh deleted file mode 100755 index aac941499..000000000 --- a/update-plugin.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -curl --header "Content-Type: application/json" \ - -u 'test@example.com:xlehGIszv9cA5XgF9rTfCmpY' \ - --request POST \ - --data '{"status":"active", "context": "view"}' \ - "http://localhost/wp-json/wp/v2/plugins/hello-dolly/hello?context=view" - #--data '{"plugin": "jetpack", "status":"inactive"}' \ From 482d547f3a849150a22647dcfc879877d48b37c7 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 14:18:00 -0400 Subject: [PATCH 21/24] Ignore failures for delete-wp-plugins-backup make task --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1c625a309..87e3460f2 100644 --- a/Makefile +++ b/Makefile @@ -208,7 +208,7 @@ restore-wp-content-plugins: docker exec -it wordpress /bin/bash -c "rm -rf ./wp-content/plugins && cp -R /tmp/backup_wp_plugins ./wp-content/plugins" delete-wp-plugins-backup: - docker exec -it wordpress /bin/bash -c "rm -rf /tmp/backup_wp_plugins" + docker exec -it wordpress /bin/bash -c "rm -rf /tmp/backup_wp_plugins" || true lint: lint-rust lint-swift From aa73608b62f1cebf63d20d84f20c0a26309e94c1 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Thu, 16 May 2024 14:24:58 -0400 Subject: [PATCH 22/24] Remove unused PluginAuthor --- wp_api/src/plugins.rs | 5 ----- wp_networking/tests/test_plugins_immut.rs | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/wp_api/src/plugins.rs b/wp_api/src/plugins.rs index 82619ea61..707788e64 100644 --- a/wp_api/src/plugins.rs +++ b/wp_api/src/plugins.rs @@ -116,11 +116,6 @@ impl PluginStatus { } } -#[derive(Debug, Serialize, Deserialize, uniffi::Record)] -pub struct PluginAuthor { - pub name: Option, -} - #[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PluginDescription { pub raw: String, diff --git a/wp_networking/tests/test_plugins_immut.rs b/wp_networking/tests/test_plugins_immut.rs index 5a143132e..edf42d204 100644 --- a/wp_networking/tests/test_plugins_immut.rs +++ b/wp_networking/tests/test_plugins_immut.rs @@ -7,6 +7,7 @@ use crate::test_helpers::{ }; pub mod test_helpers; + #[rstest] #[case(PluginListParams::default())] #[case(generate!(PluginListParams, (search, Some("foo".to_string()))))] @@ -68,7 +69,7 @@ async fn retrieve_plugin_with_edit_context( .parse(wp_api::parse_retrieve_plugin_response_with_edit_context); assert!( parsed_response.is_ok(), - "Retrievew plugin failed!\nContext: {:?}\nPlugin: {}\nResponse was: '{:?}'", + "Retrieve plugin failed!\nContext: {:?}\nPlugin: {}\nResponse was: '{:?}'", context, plugin_slug, parsed_response From b189438b7969fedadb35599446e87ca8da6c0299 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Fri, 17 May 2024 00:18:47 -0400 Subject: [PATCH 23/24] Add url encoding related unit tests for plugins endpoint --- wp_api/src/endpoint/plugins_endpoint.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/wp_api/src/endpoint/plugins_endpoint.rs b/wp_api/src/endpoint/plugins_endpoint.rs index 0f895f043..015d46e85 100644 --- a/wp_api/src/endpoint/plugins_endpoint.rs +++ b/wp_api/src/endpoint/plugins_endpoint.rs @@ -71,6 +71,8 @@ mod tests { "classic-editor/classic-editor", "/plugins/classic-editor/classic-editor" )] + #[case("foo/bar%baz", "/plugins/foo/bar%25baz")] + #[case("foo/です", "/plugins/foo/%E3%81%A7%E3%81%99")] fn delete_plugin( plugins_endpoint: PluginsEndpoint, #[case] plugin_slug: &str, @@ -103,6 +105,12 @@ mod tests { WPContext::Embed, "/plugins/classic-editor/classic-editor?context=embed" )] + #[case("foo/bar%baz", WPContext::Edit, "/plugins/foo/bar%25baz?context=edit")] + #[case( + "foo/です", + WPContext::View, + "/plugins/foo/%E3%81%A7%E3%81%99?context=view" + )] fn retrieve_plugin( plugins_endpoint: PluginsEndpoint, #[case] plugin_slug: &str, @@ -121,6 +129,8 @@ mod tests { "classic-editor/classic-editor", "/plugins/classic-editor/classic-editor" )] + #[case("foo/bar%baz", "/plugins/foo/bar%25baz")] + #[case("foo/です", "/plugins/foo/%E3%81%A7%E3%81%99")] fn update_plugin( plugins_endpoint: PluginsEndpoint, #[case] plugin_slug: &str, From 431792950ea5049353ef1813864d97264be0f0c6 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Mon, 20 May 2024 20:15:56 -0400 Subject: [PATCH 24/24] Add an assertion for plugin author for retrieve plugin test --- wp_networking/tests/test_plugins_immut.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/wp_networking/tests/test_plugins_immut.rs b/wp_networking/tests/test_plugins_immut.rs index edf42d204..e99ce147a 100644 --- a/wp_networking/tests/test_plugins_immut.rs +++ b/wp_networking/tests/test_plugins_immut.rs @@ -53,12 +53,13 @@ async fn test_plugin_list_params_parametrized( } #[rstest] -#[case(CLASSIC_EDITOR_PLUGIN_SLUG)] -#[case(HELLO_DOLLY_PLUGIN_SLUG)] +#[case(CLASSIC_EDITOR_PLUGIN_SLUG, "WordPress Contributors")] +#[case(HELLO_DOLLY_PLUGIN_SLUG, "Matt Mullenweg")] #[trace] #[tokio::test] -async fn retrieve_plugin_with_edit_context( +async fn retrieve_plugin( #[case] plugin_slug: &str, + #[case] expected_author: &str, #[values(WPContext::Edit, WPContext::Embed, WPContext::View)] context: WPContext, ) { let parsed_response = api() @@ -74,4 +75,5 @@ async fn retrieve_plugin_with_edit_context( plugin_slug, parsed_response ); + assert_eq!(expected_author, parsed_response.unwrap().author); }