Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion wp_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

use request::{
endpoint::ApiBaseUrl, plugins_request_builder::PluginsRequestBuilder,
users_request_builder::UsersRequestBuilder, RequestExecutor, WpNetworkResponse,
users_request_builder::UsersRequestBuilder, RequestExecutor, UsersRequestBuilder2,
WpNetworkResponse,
};
use std::sync::Arc;

Expand All @@ -22,6 +23,43 @@ pub mod users;
#[cfg(test)]
mod unit_test_common;

// TODO: This is a temporary type that allows building a request type
// Although we'll have a type that does that, it's unlikely that it'll look like this.
// It still does its job for now to prove that `UsersRequestBuilder2` (temporary) type is
// properly generated and utilized in `test_manual_request_builder_immut` integration tests
#[derive(Debug, uniffi::Object)]
pub struct WpApiRequestBuilder {
users: Arc<UsersRequestBuilder2>,
}

#[uniffi::export]
impl WpApiRequestBuilder {
#[uniffi::constructor]
pub fn new(
site_url: String,
authentication: WpAuthentication,
request_executor: Arc<dyn RequestExecutor>,
) -> Result<Self, WpApiError> {
let api_base_url: Arc<ApiBaseUrl> = ApiBaseUrl::try_from(site_url.as_str())
.map_err(|err| WpApiError::SiteUrlParsingError {
reason: err.to_string(),
})?
.into();
let request_builder = Arc::new(request::RequestBuilder::new(
request_executor,
authentication.clone(),
));

Ok(Self {
users: UsersRequestBuilder2::new(api_base_url.clone(), request_builder.clone()).into(),
})
}

pub fn users(&self) -> Arc<UsersRequestBuilder2> {
self.users.clone()
}
}

#[derive(Debug, uniffi::Object)]
pub struct WpRequestBuilder {
users: Arc<UsersRequestBuilder>,
Expand Down
2 changes: 2 additions & 0 deletions wp_api/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use crate::{api_error::RequestExecutionError, WpApiError, WpAuthentication};

use self::endpoint::WpEndpointUrl;

pub use endpoint::users_endpoint::generated::UsersRequestBuilder2;

pub mod endpoint;
pub mod plugins_request_builder;
pub mod users_request_builder;
Expand Down
2 changes: 1 addition & 1 deletion wp_api/src/request/endpoint/users_endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::{ApiBaseUrl, ApiEndpointUrl, UrlExtension};
// ```
// cargo expand request::endpoint::users_endpoint::generated -p wp_api
// ```
mod generated {
pub mod generated {
use super::*;

#[derive(wp_derive_request_builder::WpDerivedRequest)]
Expand Down
23 changes: 23 additions & 0 deletions wp_api/tests/reusable_test_cases.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![allow(unused_macros)]
use rstest_reuse::{self, template};

#[template]
#[rstest]
#[case(UserListParams::default())]
#[case(generate!(UserListParams, (page, Some(1))))]
#[case(generate!(UserListParams, (page, Some(2)), (per_page, Some(5))))]
#[case(generate!(UserListParams, (search, Some("foo".to_string()))))]
#[case(generate!(UserListParams, (exclude, vec![FIRST_USER_ID, SECOND_USER_ID])))]
#[case(generate!(UserListParams, (include, vec![FIRST_USER_ID])))]
#[case(generate!(UserListParams, (per_page, Some(100)), (offset, Some(20))))]
#[case(generate!(UserListParams, (order, Some(WpApiParamOrder::Asc))))]
#[case(generate!(UserListParams, (orderby, Some(WpApiParamUsersOrderBy::Id))))]
#[case(generate!(UserListParams, (order, Some(WpApiParamOrder::Desc)), (orderby, Some(WpApiParamUsersOrderBy::Email))))]
#[case(generate!(UserListParams, (slug, vec!["foo".to_string(), "bar".to_string()])))]
#[case(generate!(UserListParams, (roles, vec!["author".to_string(), "editor".to_string()])))]
#[case(generate!(UserListParams, (slug, vec!["foo".to_string(), "bar".to_string()]), (roles, vec!["author".to_string(), "editor".to_string()])))]
#[case(generate!(UserListParams, (capabilities, vec!["edit_themes".to_string(), "delete_pages".to_string()])))]
#[case::who_all_param_should_be_empty(generate!(UserListParams, (who, Some(WpApiParamUsersWho::All))))]
#[case(generate!(UserListParams, (who, Some(WpApiParamUsersWho::Authors))))]
#[case(generate!(UserListParams, (has_published_posts, Some(WpApiParamUsersHasPublishedPosts::True))))]
pub fn list_users_cases(#[case] params: UserListParams) {}
43 changes: 43 additions & 0 deletions wp_api/tests/test_manual_request_builder_immut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use integration_test_common::{
read_test_credentials_from_file, AsyncWpNetworking, FIRST_USER_ID, SECOND_USER_ID,
};
use reusable_test_cases::list_users_cases;
use rstest::*;
use rstest_reuse::{self, apply};
use std::sync::Arc;
use wp_api::{
generate,
users::UserWithEditContext,
users::{
UserListParams, WpApiParamUsersHasPublishedPosts, WpApiParamUsersOrderBy,
WpApiParamUsersWho,
},
WpApiParamOrder, WpApiRequestBuilder, WpAuthentication,
};

pub mod integration_test_common;
pub mod reusable_test_cases;

#[apply(list_users_cases)]
#[tokio::test]
async fn list_users_with_edit_context(#[case] params: UserListParams) {
let credentials = read_test_credentials_from_file();
let authentication = WpAuthentication::from_username_and_password(
credentials.admin_username,
credentials.admin_password,
);
let async_wp_networking = Arc::new(AsyncWpNetworking::default());

let request_builder = WpApiRequestBuilder::new(
credentials.site_url,
authentication,
// TODO: A request executor shouldn't be necessary, but we don't have a standalone request
// builder yet
async_wp_networking.clone(),
)
.expect("Site url is generated by our tooling");
let wp_request = request_builder.users().list_with_edit_context(&params);
let response = async_wp_networking.async_request(wp_request).await;
let result = response.unwrap().parse::<Vec<UserWithEditContext>>();
assert!(result.is_ok(), "Response was: '{:?}'", result);
}
23 changes: 2 additions & 21 deletions wp_api/tests/test_users_immut.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use reusable_test_cases::list_users_cases;
use rstest::*;
use rstest_reuse::{self, apply, template};
use wp_api::{
Expand All @@ -14,6 +15,7 @@ use crate::integration_test_common::{
};

pub mod integration_test_common;
pub mod reusable_test_cases;

#[apply(filter_fields_cases)]
#[tokio::test]
Expand Down Expand Up @@ -248,27 +250,6 @@ fn validate_sparse_user_fields(user: &SparseUser, fields: &[SparseUserField]) {
);
}

#[template]
#[rstest]
#[case(UserListParams::default())]
#[case(generate!(UserListParams, (page, Some(1))))]
#[case(generate!(UserListParams, (page, Some(2)), (per_page, Some(5))))]
#[case(generate!(UserListParams, (search, Some("foo".to_string()))))]
#[case(generate!(UserListParams, (exclude, vec![FIRST_USER_ID, SECOND_USER_ID])))]
#[case(generate!(UserListParams, (include, vec![FIRST_USER_ID])))]
#[case(generate!(UserListParams, (per_page, Some(100)), (offset, Some(20))))]
#[case(generate!(UserListParams, (order, Some(WpApiParamOrder::Asc))))]
#[case(generate!(UserListParams, (orderby, Some(WpApiParamUsersOrderBy::Id))))]
#[case(generate!(UserListParams, (order, Some(WpApiParamOrder::Desc)), (orderby, Some(WpApiParamUsersOrderBy::Email))))]
#[case(generate!(UserListParams, (slug, vec!["foo".to_string(), "bar".to_string()])))]
#[case(generate!(UserListParams, (roles, vec!["author".to_string(), "editor".to_string()])))]
#[case(generate!(UserListParams, (slug, vec!["foo".to_string(), "bar".to_string()]), (roles, vec!["author".to_string(), "editor".to_string()])))]
#[case(generate!(UserListParams, (capabilities, vec!["edit_themes".to_string(), "delete_pages".to_string()])))]
#[case::who_all_param_should_be_empty(generate!(UserListParams, (who, Some(WpApiParamUsersWho::All))))]
#[case(generate!(UserListParams, (who, Some(WpApiParamUsersWho::Authors))))]
#[case(generate!(UserListParams, (has_published_posts, Some(WpApiParamUsersHasPublishedPosts::True))))]
fn list_users_cases(#[case] params: UserListParams) {}

#[template]
#[rstest]
#[case(None)]
Expand Down
96 changes: 86 additions & 10 deletions wp_derive_request_builder/src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,89 @@ use crate::{

mod helpers_to_generate_tokens;

pub(crate) fn generate_types(parsed_enum: &ParsedEnum) -> syn::Result<TokenStream> {
pub(crate) fn generate_types(parsed_enum: &ParsedEnum) -> TokenStream {
let config = Config::new(parsed_enum);
TokenStream::from_iter(
&mut [
generate_endpoint_type(&config, parsed_enum),
generate_request_builder(&config, parsed_enum),
]
.into_iter(),
)
}

fn generate_request_builder(config: &Config, parsed_enum: &ParsedEnum) -> TokenStream {
let api_base_url_type = &config.api_base_url_type;
let endpoint_ident = &config.endpoint_ident;
let request_builder_ident = &config.request_builder_ident;
let request_builder_type = &config.request_builder_type;
let wp_network_request_type = &config.wp_network_request_type;

let functions = parsed_enum.variants.iter().map(|variant| {
let url_parts = variant.attr.url_parts.as_slice();
let params_type = &variant.attr.params;

ContextAndFilterHandler::from_request_type(variant.attr.request_type)
.into_iter()
.map(|context_and_filter_handler| {
let url_from_endpoint = fn_body_get_url_from_endpoint(
&variant.variant_ident,
url_parts,
params_type,
variant.attr.request_type,
context_and_filter_handler,
);
let fn_signature = fn_signature(
PartOf::RequestBuilder,
&variant.variant_ident,
url_parts,
params_type,
variant.attr.request_type,
context_and_filter_handler,
&config.sparse_field_type,
);
let fn_body =
fn_body_build_request_from_url(params_type, variant.attr.request_type);
quote! {
pub #fn_signature -> #wp_network_request_type {
#url_from_endpoint
#fn_body
}
}
})
.collect::<TokenStream>()
});

Ok(generate_endpoint_type(&config, parsed_enum))
quote! {
#[derive(Debug, uniffi::Object)]
pub struct #request_builder_ident {
endpoint: #endpoint_ident,
request_builder: #request_builder_type,
}
impl #request_builder_ident {
pub(crate) fn new(api_base_url: #api_base_url_type, request_builder: #request_builder_type) -> Self {
Self {
endpoint: #endpoint_ident::new(api_base_url),
request_builder,
}
}
}
#[uniffi::export]
impl #request_builder_ident {
#(#functions)*
}
}
}

fn generate_endpoint_type(config: &Config, parsed_enum: &ParsedEnum) -> TokenStream {
let api_base_url_type = &config.api_base_url_type;
let endpoint_ident = format_ident!("{}Endpoint", parsed_enum.enum_ident);
let endpoint_ident = &config.endpoint_ident;

let functions = parsed_enum.variants.iter().map(|variant| {
let url_parts = variant.attr.url_parts.as_slice();
let params_type = &variant.attr.params;
let request_type = variant.attr.request_type;
let url_from_endpoint = fn_body_get_url_from_api_base_url(url_parts);
let url_from_api_base_url = fn_body_get_url_from_api_base_url(url_parts);
let query_pairs = fn_body_query_pairs(params_type, request_type);

ContextAndFilterHandler::from_request_type(request_type)
Expand All @@ -51,7 +119,7 @@ fn generate_endpoint_type(config: &Config, parsed_enum: &ParsedEnum) -> TokenStr
fn_body_fields_query_pairs(&config.crate_ident, context_and_filter_handler);
quote! {
pub #fn_signature -> #api_endpoint_url_type {
#url_from_endpoint
#url_from_api_base_url
#context_query_pair
#query_pairs
#fields_query_pairs
Expand Down Expand Up @@ -139,13 +207,15 @@ impl Display for WpContext {

#[derive(Debug)]
pub struct Config {
pub crate_ident: Ident,
pub api_base_url_type: TokenStream,
pub api_endpoint_url_type: TokenStream,
pub request_builder_type: TokenStream,
pub crate_ident: Ident,
pub endpoint_ident: Ident,
pub request_builder_ident: Ident,
pub request_builder_type: TokenStream,
pub sparse_field_type: SparseFieldAttr,
pub wp_api_error_type: TokenStream,
pub wp_network_request_type: TokenStream,
}

impl Config {
Expand All @@ -162,14 +232,20 @@ impl Config {
quote! { std::sync::Arc<#crate_ident::request::endpoint::ApiBaseUrl> };
let api_endpoint_url_type = quote! { #crate_ident::request::endpoint::ApiEndpointUrl };
let request_builder_type = quote! { std::sync::Arc<#crate_ident::request::RequestBuilder> };
let wp_api_error_type = quote! { #crate_ident::WpApiError };
let wp_network_request_type = quote! { #crate_ident::request::WpNetworkRequest };
Self {
crate_ident,
api_base_url_type,
api_endpoint_url_type,
request_builder_type,
crate_ident,
endpoint_ident: format_ident!("{}Endpoint", parsed_enum.enum_ident),
request_builder_ident: format_ident!("{}Builder", parsed_enum.enum_ident),
// TODO: We use `2` suffix here to prevent the name clash with the current
// implementation
request_builder_ident: format_ident!("{}Builder2", parsed_enum.enum_ident),
request_builder_type,
sparse_field_type: parsed_enum.sparse_field_attr.clone(),
wp_api_error_type,
wp_network_request_type,
}
}
}
Loading