diff --git a/Makefile b/Makefile index be0afeff0..23f10831a 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ android_project_root := ./native/android android_generated_source_path := $(android_project_root)/lib/build/generated/source jni_libs_root := $(android_project_root)/lib/src/main/jniLibs -udl_path := wp_api/src/wp_api.udl # The directory where the git repo is mounted in the docker container docker_container_repo_dir=/app @@ -35,7 +34,7 @@ bindings: #wp_api cargo run --release --bin uniffi_bindgen generate --library ./target/release/libwp_api.dylib --out-dir $(android_generated_source_path) --language kotlin - cargo run --release --bin uniffi_bindgen generate wp_api/src/wp_api.udl --out-dir ./target/swift-bindings --language swift + cargo run --release --bin uniffi_bindgen generate --library ./target/release/libwp_api.dylib --out-dir ./target/swift-bindings --language swift cp target/swift-bindings/wp_api.swift native/swift/Sources/wordpress-api-wrapper/wp_api.swift sed -i '' 's/wp_apiFFI/libwordpressFFI/g' native/swift/Sources/wordpress-api-wrapper/wp_api.swift diff --git a/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt b/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt index 7bac8a6cb..bfa1f4658 100644 --- a/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt +++ b/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt @@ -24,7 +24,7 @@ class LibraryTest { fun testBasicPostListRequest() { val request = library.postListRequest() assert(request.method == RequestMethod.GET) - assert(request.url == "$siteUrl/wp-json/wp/v2/posts?context=edit") + assert(request.url == "$siteUrl/wp-json/wp/v2/posts?context=edit&page=1&per_page=10") } @Test diff --git a/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt b/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt index 34080e37c..d41780132 100644 --- a/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt +++ b/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt @@ -17,7 +17,8 @@ class Library(siteUrl: String, authentication: WpAuthentication) { private val wpApiHelper = WpApiHelper(siteUrl, authentication) private val client = OkHttpClient() - fun postListRequest(): WpNetworkRequest = wpApiHelper.postListRequest(PostListParams()) + fun postListRequest(): WpNetworkRequest = + wpApiHelper.postListRequest(PostListParams()) fun makePostListRequest(): PostListResponse { val wpNetworkRequest = postListRequest() @@ -32,7 +33,11 @@ class Library(siteUrl: String, authentication: WpAuthentication) { } client.newCall(requestBuilder.build()).execute().use { response -> - return WpNetworkResponse(response.body!!.bytes(), response.code.toUShort(), null) + return WpNetworkResponse( + body = response.body!!.bytes(), + statusCode = response.code.toUShort(), + headerMap = null + ) } } } diff --git a/native/swift/Sources/wordpress-api/WordPressAPI.swift b/native/swift/Sources/wordpress-api/WordPressAPI.swift index befa53caa..63d0361ab 100644 --- a/native/swift/Sources/wordpress-api/WordPressAPI.swift +++ b/native/swift/Sources/wordpress-api/WordPressAPI.swift @@ -12,7 +12,7 @@ public struct WordPressAPI { public init(urlSession: URLSession, baseUrl: URL, authenticationStategy: WpAuthentication) { self.urlSession = urlSession - self.helper = WpApiHelper(url: baseUrl.absoluteString, authentication: authenticationStategy) + self.helper = WpApiHelper(siteUrl: baseUrl.absoluteString, authentication: authenticationStategy) } package func perform(request: WpNetworkRequest) async throws -> WpNetworkResponse { diff --git a/wp_api/build.rs b/wp_api/build.rs deleted file mode 100644 index 8dc1a561a..000000000 --- a/wp_api/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - uniffi::generate_scaffolding("src/wp_api.udl").unwrap(); -} diff --git a/wp_api/src/api_error.rs b/wp_api/src/api_error.rs index a398caab0..b2eaa0ad6 100644 --- a/wp_api/src/api_error.rs +++ b/wp_api/src/api_error.rs @@ -1,6 +1,6 @@ use http::StatusCode; -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, uniffi::Error)] pub enum WPApiError { #[error( "Client error with type '{:?}' and status_code '{}'", @@ -19,7 +19,7 @@ pub enum WPApiError { UnknownError, } -#[derive(Debug)] +#[derive(Debug, uniffi::Enum)] pub enum ClientErrorType { BadRequest, Unauthorized, diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 0b158d30a..b4f4f8389 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -11,12 +11,15 @@ pub mod api_error; pub mod pages; pub mod posts; +#[derive(uniffi::Object)] pub struct WPApiHelper { site_url: Url, authentication: WPAuthentication, } +#[uniffi::export] impl WPApiHelper { + #[uniffi::constructor] pub fn new(site_url: String, authentication: WPAuthentication) -> Self { let url = Url::parse(site_url.as_str()).unwrap(); @@ -53,15 +56,10 @@ impl WPApiHelper { format!("Basic {}", self.authentication.auth_token), ); - if let Some(page) = params.page { - url.query_pairs_mut() - .append_pair("page", page.to_string().as_str()); - } - - if let Some(per_page) = params.per_page { - url.query_pairs_mut() - .append_pair("per_page", per_page.to_string().as_str()); - } + url.query_pairs_mut() + .append_pair("page", params.page.to_string().as_str()); + url.query_pairs_mut() + .append_pair("per_page", params.per_page.to_string().as_str()); WPNetworkRequest { method: RequestMethod::GET, @@ -71,12 +69,13 @@ impl WPApiHelper { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, uniffi::Record)] // TODO: This will probably become an `enum` where we support multiple authentication types. pub struct WPAuthentication { pub auth_token: String, } +#[derive(uniffi::Enum)] pub enum RequestMethod { GET, POST, @@ -84,6 +83,7 @@ pub enum RequestMethod { DELETE, } +#[derive(uniffi::Record)] pub struct WPNetworkRequest { pub method: RequestMethod, pub url: String, @@ -95,9 +95,10 @@ pub struct WPNetworkRequest { pub header_map: Option>, } +#[derive(uniffi::Record)] pub struct WPNetworkResponse { - pub status_code: u16, pub body: Vec, + pub status_code: u16, // TODO: We probably want to implement a specific type for these headers instead of using a // regular HashMap. // @@ -106,6 +107,7 @@ pub struct WPNetworkResponse { pub header_map: Option>, } +#[uniffi::export] pub fn parse_post_list_response( response: WPNetworkResponse, ) -> Result { @@ -156,4 +158,4 @@ pub fn extract_link_header(response: &WPNetworkResponse) -> Option { None } -uniffi::include_scaffolding!("wp_api"); +uniffi::setup_scaffolding!("wp_api"); diff --git a/wp_api/src/pages.rs b/wp_api/src/pages.rs index 834a13fa5..802c7af81 100644 --- a/wp_api/src/pages.rs +++ b/wp_api/src/pages.rs @@ -1,16 +1,15 @@ -pub struct PageRequestBuilder {} - +#[derive(uniffi::Record)] pub struct PageListParams { pub page: Option, pub per_page: Option, } -#[derive(Debug)] +#[derive(Debug, uniffi::Record)] pub struct PageListResponse { pub page_list: Option>, } -#[derive(Debug)] +#[derive(Debug, uniffi::Record)] pub struct PageObject { pub id: Option, pub title: Option, diff --git a/wp_api/src/posts.rs b/wp_api/src/posts.rs index 23db6e618..e26ff66ca 100644 --- a/wp_api/src/posts.rs +++ b/wp_api/src/posts.rs @@ -2,70 +2,81 @@ use serde::{Deserialize, Serialize}; pub trait PostNetworkingInterface: Send + Sync {} -#[derive(Default)] // The default has `None` for all +#[derive(Default, uniffi::Record)] pub struct PostListParams { - pub page: Option, - pub per_page: Option, + #[uniffi(default = 1)] + pub page: u32, + #[uniffi(default = 10)] + pub per_page: u32, } +#[derive(uniffi::Record)] pub struct PostCreateParams { pub title: Option, pub content: Option, } +#[derive(uniffi::Record)] pub struct PostRetrieveParams { pub password: Option, } +#[derive(uniffi::Record)] pub struct PostUpdateParams { pub title: Option, pub content: Option, } +#[derive(uniffi::Record)] pub struct PostDeleteParams { pub force: Option, } +#[derive(uniffi::Record)] pub struct PostListRequest { pub params: Option, } +#[derive(uniffi::Record)] pub struct PostCreateRequest { pub params: Option, } +#[derive(uniffi::Record)] pub struct PostRetrieveRequest { pub params: Option, } +#[derive(uniffi::Record)] pub struct PostUpdateRequest { pub params: Option, } +#[derive(uniffi::Record)] pub struct PostDeleteRequest { pub params: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostListResponse { pub post_list: Option>, pub next_page: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostCreateResponse { pub post: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostRetrieveResponse { pub post: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostUpdateResponse { pub post: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostDeleteResponse { pub post: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostObject { pub id: Option, pub date: Option, @@ -92,19 +103,19 @@ pub struct PostObject { pub tags: Option>, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostGuid { pub raw: Option, pub rendered: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostTitle { pub raw: Option, pub rendered: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostContent { pub raw: Option, pub rendered: Option, @@ -112,14 +123,14 @@ pub struct PostContent { pub block_version: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostExcerpt { pub raw: Option, pub rendered: Option, pub protected: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct PostMeta { pub footnotes: Option, } diff --git a/wp_api/src/wp_api.udl b/wp_api/src/wp_api.udl deleted file mode 100644 index c13c70e3e..000000000 --- a/wp_api/src/wp_api.udl +++ /dev/null @@ -1,159 +0,0 @@ -namespace wp_api { - [Throws=WPApiError] - PostListResponse parse_post_list_response(WPNetworkResponse response); -}; - -enum ClientErrorType { - "BadRequest", - "Unauthorized", - "TooManyRequests", - "Other", -}; - -[Error] -interface WPApiError { - ClientError(ClientErrorType error_type, u16 status_code); - ServerError(u16 status_code); - ParsingError(string reason, string response); - UnknownError(); -}; - -dictionary WPAuthentication { - string auth_token; -}; - -enum RequestMethod { - "GET", - "POST", - "PUT", - "DELETE" -}; - -interface WPApiHelper { - constructor(string url, WPAuthentication authentication); - WPNetworkRequest post_list_request(PostListParams params); - WPNetworkRequest raw_request(string url); -}; - -dictionary WPNetworkRequest { - RequestMethod method; - string url; - record? header_map; -}; - -dictionary WPNetworkResponse { - bytes body; - u16 status_code; - record? header_map; -}; - -dictionary PostListResponse { - sequence? post_list; - string? next_page; -}; -dictionary PostCreateResponse { - PostObject? post; -}; -dictionary PostRetrieveResponse { - PostObject? post; -}; -dictionary PostUpdateResponse { - PostObject? post; -}; -dictionary PostDeleteResponse { - PostObject? post; -}; - -dictionary PostObject { - u32? id; - string? date; - string? date_gmt; - PostGuid? guid; - string? modified; - string? modified_gmt; - string? password; - string? slug; - string? status; - string? link; - PostTitle? title; - PostContent? content; - PostExcerpt? excerpt; - u32? author; - u32? featured_media; - string? comment_status; - string? ping_status; - boolean? sticky; - string? template; - string? format; - PostMeta? meta; - sequence? categories; - sequence? tags; -}; - -dictionary PostGuid { - string? raw; - string? rendered; -}; - -dictionary PostTitle { - string? raw; - string? rendered; -}; - -dictionary PostContent { - string? raw; - string? rendered; - boolean? protected; - u32? block_version; -}; - -dictionary PostExcerpt { - string? raw; - string? rendered; - boolean? protected; -}; - -dictionary PostMeta { - string? footnotes; -}; - -// We should check if it's possible to use the same params for update & create -dictionary PostCreateParams { - string? title; - string? content; -}; - -dictionary PostListParams { - u32? page = null; - u32? per_page = null; -}; - -dictionary PostRetrieveParams { - string? password; -}; - -dictionary PostUpdateParams { - string? title; - string? content; -}; - -dictionary PostDeleteParams { - boolean? force; -}; - -// --------------------------------------------------------------------------- - -dictionary PageListParams { - u32? page; - u32? per_page; -}; - -dictionary PageListResponse { - sequence? page_list; -}; - -dictionary PageObject { - u32? id; - string? title; - string? content; -};