Skip to content

Commit 0722c3f

Browse files
authored
Plugins endpoint (#90)
* Adds SparsePlugin * Adds plugins list endpoint * Implement list plugins request and its parsers * Replace plugin parsers with add_uniffi_exported_parser * Replace plugin_list_params! with generate! macro * Use extracted test helpers for plugins * Implement create plugin request * Implement run_and_restore_wp_content_plugins * Implements retrieve plugin * Fix the retrieve plugin endpoint and its return type * A temporary helper bash script for fetching plugins * Disable restoring wp-content plugins from plugins_mut tests * Add a separate parser for create plugin response * Fix retrieve plugins endpoint * Implement update plugin request * Add an integration test for update plugin * Implement delete plugin request * Improve unit & immut integration tests for plugins * Fix backup/restoration of wp-content/plugins for integration tests * Remove temporary scripts used to test plugins * Ignore failures for delete-wp-plugins-backup make task * Remove unused PluginAuthor * Add url encoding related unit tests for plugins endpoint * Add an assertion for plugin author for retrieve plugin test
1 parent 540de90 commit 0722c3f

File tree

10 files changed

+532
-3
lines changed

10 files changed

+532
-3
lines changed

.buildkite/setup-test-site.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,7 @@ wp plugin delete wordpress-importer
4242
## Create an Application password for a subscriber user, and store it where it can be used by the test suite
4343
wp user application-password create themedemos test --porcelain
4444
} >> /tmp/test_credentials
45+
46+
## Used for integration tests
47+
wp plugin install hello-dolly --activate
48+
wp plugin install classic-editor

Makefile

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ test-server: stop-server
192192
docker-compose up -d
193193
docker-compose run wpcli
194194

195-
stop-server:
195+
stop-server: delete-wp-plugins-backup
196196
docker-compose down
197197

198198
dump-mysql:
@@ -201,6 +201,15 @@ dump-mysql:
201201
restore-mysql:
202202
docker exec -it wordpress-rs-mysql-1 /bin/bash -c "mysql --defaults-extra-file=mysql_config/config.cnf --database wordpress < dump.sql"
203203

204+
backup-wp-content-plugins:
205+
docker exec -it wordpress /bin/bash -c "cp -R ./wp-content/plugins /tmp/backup_wp_plugins"
206+
207+
restore-wp-content-plugins:
208+
docker exec -it wordpress /bin/bash -c "rm -rf ./wp-content/plugins && cp -R /tmp/backup_wp_plugins ./wp-content/plugins"
209+
210+
delete-wp-plugins-backup:
211+
docker exec -it wordpress /bin/bash -c "rm -rf /tmp/backup_wp_plugins" || true
212+
204213
lint: lint-rust lint-swift
205214

206215
lint-rust:

wp_api/src/endpoint.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use url::Url;
22

3+
pub use plugins_endpoint::*;
34
pub use users_endpoint::*;
45

6+
mod plugins_endpoint;
57
mod users_endpoint;
68

79
const WP_JSON_PATH_SEGMENTS: [&str; 3] = ["wp-json", "wp", "v2"];
@@ -47,13 +49,15 @@ impl ApiBaseUrl {
4749
pub struct ApiEndpoint {
4850
pub base_url: ApiBaseUrl,
4951
pub users: UsersEndpoint,
52+
pub plugins: PluginsEndpoint,
5053
}
5154

5255
impl ApiEndpoint {
5356
pub fn new(api_base_url: ApiBaseUrl) -> Self {
5457
Self {
5558
base_url: api_base_url.clone(),
5659
users: UsersEndpoint::new(api_base_url.clone()),
60+
plugins: PluginsEndpoint::new(api_base_url.clone()),
5761
}
5862
}
5963

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
use url::Url;
2+
3+
use crate::{plugins::PluginListParams, ApiBaseUrl, WPContext};
4+
5+
pub struct PluginsEndpoint {
6+
api_base_url: ApiBaseUrl,
7+
}
8+
9+
impl PluginsEndpoint {
10+
pub fn new(api_base_url: ApiBaseUrl) -> Self {
11+
Self { api_base_url }
12+
}
13+
14+
pub fn create(&self) -> Url {
15+
self.plugins_base_url()
16+
}
17+
18+
pub fn delete(&self, plugin: &str) -> Url {
19+
self.plugins_url_with_slug(plugin)
20+
}
21+
22+
pub fn list(&self, context: WPContext, params: Option<&PluginListParams>) -> Url {
23+
let mut url = self.plugins_base_url();
24+
url.query_pairs_mut()
25+
.append_pair("context", context.as_str());
26+
if let Some(params) = params {
27+
url.query_pairs_mut().extend_pairs(params.query_pairs());
28+
}
29+
url
30+
}
31+
32+
pub fn retrieve(&self, context: WPContext, plugin: &str) -> Url {
33+
let mut url = self.plugins_url_with_slug(plugin);
34+
url.query_pairs_mut()
35+
.append_pair("context", context.as_str());
36+
url
37+
}
38+
39+
pub fn update(&self, plugin: &str) -> Url {
40+
self.plugins_url_with_slug(plugin)
41+
}
42+
43+
fn plugins_base_url(&self) -> Url {
44+
self.api_base_url.by_appending("plugins")
45+
}
46+
47+
fn plugins_url_with_slug(&self, plugin: &str) -> Url {
48+
self.api_base_url
49+
// The '/' character has to be preserved and not get encoded
50+
.by_extending(["plugins"].into_iter().chain(plugin.split('/')))
51+
}
52+
}
53+
54+
#[cfg(test)]
55+
mod tests {
56+
use super::*;
57+
use crate::{
58+
endpoint::tests::{fixture_api_base_url, validate_endpoint},
59+
generate, ApiEndpoint, PluginStatus,
60+
};
61+
use rstest::*;
62+
63+
#[rstest]
64+
fn create_plugin(plugins_endpoint: PluginsEndpoint) {
65+
validate_endpoint(plugins_endpoint.create(), "/plugins");
66+
}
67+
68+
#[rstest]
69+
#[case("hello-dolly/hello", "/plugins/hello-dolly/hello")]
70+
#[case(
71+
"classic-editor/classic-editor",
72+
"/plugins/classic-editor/classic-editor"
73+
)]
74+
#[case("foo/bar%baz", "/plugins/foo/bar%25baz")]
75+
#[case("foo/です", "/plugins/foo/%E3%81%A7%E3%81%99")]
76+
fn delete_plugin(
77+
plugins_endpoint: PluginsEndpoint,
78+
#[case] plugin_slug: &str,
79+
#[case] expected_path: &str,
80+
) {
81+
validate_endpoint(plugins_endpoint.delete(plugin_slug), expected_path);
82+
}
83+
84+
#[rstest]
85+
#[case(WPContext::Edit, generate!(PluginListParams, (search, Some("foo".to_string()))), "/plugins?context=edit&search=foo")]
86+
#[case(WPContext::Embed, generate!(PluginListParams, (status, Some(PluginStatus::Active))), "/plugins?context=embed&status=active")]
87+
#[case(WPContext::View, generate!(PluginListParams, (search, Some("foo".to_string())), (status, Some(PluginStatus::Inactive))), "/plugins?context=view&search=foo&status=inactive")]
88+
fn list_plugins_with_params(
89+
plugins_endpoint: PluginsEndpoint,
90+
#[case] context: WPContext,
91+
#[case] params: PluginListParams,
92+
#[case] expected_path: &str,
93+
) {
94+
validate_endpoint(plugins_endpoint.list(context, Some(&params)), expected_path);
95+
}
96+
97+
#[rstest]
98+
#[case(
99+
"hello-dolly/hello",
100+
WPContext::View,
101+
"/plugins/hello-dolly/hello?context=view"
102+
)]
103+
#[case(
104+
"classic-editor/classic-editor",
105+
WPContext::Embed,
106+
"/plugins/classic-editor/classic-editor?context=embed"
107+
)]
108+
#[case("foo/bar%baz", WPContext::Edit, "/plugins/foo/bar%25baz?context=edit")]
109+
#[case(
110+
"foo/です",
111+
WPContext::View,
112+
"/plugins/foo/%E3%81%A7%E3%81%99?context=view"
113+
)]
114+
fn retrieve_plugin(
115+
plugins_endpoint: PluginsEndpoint,
116+
#[case] plugin_slug: &str,
117+
#[case] context: WPContext,
118+
#[case] expected_path: &str,
119+
) {
120+
validate_endpoint(
121+
plugins_endpoint.retrieve(context, plugin_slug),
122+
expected_path,
123+
);
124+
}
125+
126+
#[rstest]
127+
#[case("hello-dolly/hello", "/plugins/hello-dolly/hello")]
128+
#[case(
129+
"classic-editor/classic-editor",
130+
"/plugins/classic-editor/classic-editor"
131+
)]
132+
#[case("foo/bar%baz", "/plugins/foo/bar%25baz")]
133+
#[case("foo/です", "/plugins/foo/%E3%81%A7%E3%81%99")]
134+
fn update_plugin(
135+
plugins_endpoint: PluginsEndpoint,
136+
#[case] plugin_slug: &str,
137+
#[case] expected_path: &str,
138+
) {
139+
validate_endpoint(plugins_endpoint.update(plugin_slug), expected_path);
140+
}
141+
142+
#[fixture]
143+
fn plugins_endpoint(fixture_api_base_url: ApiBaseUrl) -> PluginsEndpoint {
144+
ApiEndpoint::new(fixture_api_base_url).plugins
145+
}
146+
}

wp_api/src/lib.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub use api_error::*;
77
pub use endpoint::*;
88
pub use login::*;
99
pub use pages::*;
10+
pub use plugins::*;
1011
pub use posts::*;
1112
pub use url::*;
1213
pub use users::*;
@@ -15,6 +16,7 @@ pub mod api_error;
1516
pub mod endpoint;
1617
pub mod login;
1718
pub mod pages;
19+
pub mod plugins;
1820
pub mod posts;
1921
pub mod url;
2022
pub mod users;
@@ -223,6 +225,63 @@ impl WPApiHelper {
223225
}
224226
}
225227

228+
pub fn list_plugins_request(
229+
&self,
230+
context: WPContext,
231+
params: &Option<PluginListParams>, // UniFFI doesn't support Option<&T>
232+
) -> WPNetworkRequest {
233+
WPNetworkRequest {
234+
method: RequestMethod::GET,
235+
url: self
236+
.api_endpoint
237+
.plugins
238+
.list(context, params.as_ref())
239+
.into(),
240+
header_map: self.header_map(),
241+
body: None,
242+
}
243+
}
244+
245+
pub fn create_plugin_request(&self, params: &PluginCreateParams) -> WPNetworkRequest {
246+
WPNetworkRequest {
247+
method: RequestMethod::POST,
248+
url: self.api_endpoint.plugins.create().into(),
249+
header_map: self.header_map_for_post_request(),
250+
body: serde_json::to_vec(&params).ok(),
251+
}
252+
}
253+
254+
pub fn retrieve_plugin_request(&self, context: WPContext, plugin: &str) -> WPNetworkRequest {
255+
WPNetworkRequest {
256+
method: RequestMethod::GET,
257+
url: self.api_endpoint.plugins.retrieve(context, plugin).into(),
258+
header_map: self.header_map(),
259+
body: None,
260+
}
261+
}
262+
263+
pub fn update_plugin_request(
264+
&self,
265+
plugin: &str,
266+
params: PluginUpdateParams,
267+
) -> WPNetworkRequest {
268+
WPNetworkRequest {
269+
method: RequestMethod::POST,
270+
url: self.api_endpoint.plugins.update(plugin).into(),
271+
header_map: self.header_map_for_post_request(),
272+
body: serde_json::to_vec(&params).ok(),
273+
}
274+
}
275+
276+
pub fn delete_plugin_request(&self, plugin: &str) -> WPNetworkRequest {
277+
WPNetworkRequest {
278+
method: RequestMethod::DELETE,
279+
url: self.api_endpoint.plugins.delete(plugin).into(),
280+
header_map: self.header_map(),
281+
body: None,
282+
}
283+
}
284+
226285
fn header_map(&self) -> HashMap<String, String> {
227286
let mut header_map = HashMap::new();
228287
header_map.insert(

0 commit comments

Comments
 (0)