diff --git a/CLAUDE.md b/CLAUDE.md index 4fb9745f4..76b9e5d64 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -97,26 +97,21 @@ WordPress REST API returns different fields depending on the `context` parameter 3. **Define parameter types** for list/create/update operations: ```rust - #[derive(Debug, Default, PartialEq, Eq, uniffi::Record)] + #[derive(Debug, Default, PartialEq, Eq, uniffi::Record, WpDeriveParamsField)] + #[supports_pagination(true)] // or false if endpoint doesn't support pagination pub struct UserListParams { #[uniffi(default = None)] pub page: Option, + #[uniffi(default = [])] + pub exclude: Vec, // ... other fields } ``` - -4. **Implement query parameter handling**: - - Create a `{Type}ListParamsField` enum: - ```rust - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, IntoStaticStr)] - enum UserListParamsField { - #[strum(serialize = "page")] - Page, - // ... other fields - } - ``` - - Implement `AppendUrlQueryPairs` and `FromUrlQueryPairs` traits - - Import helpers: `crate::url_query::{AppendUrlQueryPairs, FromUrlQueryPairs, QueryPairs, QueryPairsExtension, UrlQueryPairsMap}` + - Use `WpDeriveParamsField` macro to automatically generate field enum and query parameter handling + - Add `#[supports_pagination(true/false)]` attribute to indicate pagination support + - Use `#[field_name("custom_name")]` attribute if the API field name differs from the struct field name + - **IMPORTANT**: For array/list parameters, use `Vec` with `#[uniffi(default = [])]`, NOT `Option>` with `#[uniffi(default = None)]` + - Import: `use wp_derive::WpDeriveParamsField;` **Special parameter types:** diff --git a/native/swift/Example/Example/ExampleApp.swift b/native/swift/Example/Example/ExampleApp.swift index 03af99cb4..3d4b372bd 100644 --- a/native/swift/Example/Example/ExampleApp.swift +++ b/native/swift/Example/Example/ExampleApp.swift @@ -164,25 +164,31 @@ struct ExampleApp: App { .map(\.value.asListViewData) }, category: .posts)) + baseData.append(RootListData(name: "Navigations", callback: { + try await WordPressAPI.globalInstance.navigations.listWithEditContext(params: NavigationListParams()) + .data + .map(\.asListViewData) + }, category: .navigation)) + baseData.append(RootListData(name: "Menus", callback: { try await WordPressAPI.globalInstance.navMenus.listWithEditContext(params: NavMenuListParams()) .data .map(\.asListViewData) - }, category: .system)) + }, category: .navigation)) baseData.append(RootListData(name: "Menu Items", sequence: { let sequence = try await WordPressAPI.globalInstance.navMenuItems .sequenceWithEditContext(params: NavMenuItemListParams()) return ListViewSequence(underlyingSequence: sequence) - }, category: .system)) + }, category: .navigation)) baseData.append(RootListData(name: "Menu Locations", callback: { try await WordPressAPI.globalInstance.menuLocations.listWithEditContext() .data .locations .map(\.value.asListViewData) - }, category: .system)) + }, category: .navigation)) return baseData } diff --git a/native/swift/Example/Example/ListViewData.swift b/native/swift/Example/Example/ListViewData.swift index 79aeaf13a..4fd9f9c47 100644 --- a/native/swift/Example/Example/ListViewData.swift +++ b/native/swift/Example/Example/ListViewData.swift @@ -183,6 +183,17 @@ extension PostStatusWithEditContext: ListViewDataConvertable { } } +extension NavigationWithEditContext: ListViewDataConvertable { + var asListViewData: ListViewData { + let title = switch self.title { + case .object(let title): title.rendered + case .string(let string): string + } + + return ListViewData(id: self.id.description, title: title ?? "Unknown", subtitle: self.slug, fields: [:]) + } +} + extension NavMenuWithEditContext: ListViewDataConvertable { var asListViewData: ListViewData { ListViewData(id: self.id.description, title: self.name, subtitle: self.slug, fields: [:]) diff --git a/native/swift/Example/Example/UI/RootListView.swift b/native/swift/Example/Example/UI/RootListView.swift index 5561eb00a..96b2cb1fe 100644 --- a/native/swift/Example/Example/UI/RootListView.swift +++ b/native/swift/Example/Example/UI/RootListView.swift @@ -55,6 +55,7 @@ enum RootListData: Identifiable, Sendable { enum Category: Hashable, Identifiable, CaseIterable { case posts case taxonomies + case navigation case system var id: String { @@ -65,6 +66,7 @@ enum RootListData: Identifiable, Sendable { return switch self { case .posts: "Posts" case .taxonomies: "Taxonomies" + case .navigation: "Navigation" case .system: "System" } } diff --git a/native/swift/Sources/wordpress-api/Exports.swift b/native/swift/Sources/wordpress-api/Exports.swift index 58216e870..07ace83ae 100644 --- a/native/swift/Sources/wordpress-api/Exports.swift +++ b/native/swift/Sources/wordpress-api/Exports.swift @@ -194,6 +194,17 @@ public typealias TermsRequestListWithEmbedContextResponse = WordPressAPIInternal // MARK: - Taxonomies public typealias TaxonomyListParams = WordPressAPIInternal.TaxonomyListParams +// MARK: Navigation +public typealias NavigationStatus = WordPressAPIInternal.NavigationStatus +public typealias NavigationListParams = WordPressAPIInternal.NavigationListParams +public typealias NavigationCreateParams = WordPressAPIInternal.NavigationCreateParams +public typealias NavigationUpdateParams = WordPressAPIInternal.NavigationUpdateParams +public typealias NavigationRetrieveParams = WordPressAPIInternal.NavigationRetrieveParams +public typealias NavigationRequestExecutor = WordPressAPIInternal.NavigationsRequestExecutor +public typealias NavigationWithEditContext = WordPressAPIInternal.NavigationWithEditContext +public typealias NavigationWithViewContext = WordPressAPIInternal.NavigationWithViewContext +public typealias NavigationWithEmbedContext = WordPressAPIInternal.NavigationWithEmbedContext + // MARK: - Menus public typealias SparseNavMenu = WordPressAPIInternal.SparseNavMenu public typealias NavMenuListParams = WordPressAPIInternal.NavMenuListParams diff --git a/native/swift/Sources/wordpress-api/WordPressAPI.swift b/native/swift/Sources/wordpress-api/WordPressAPI.swift index 6bd680cf4..6254d546c 100644 --- a/native/swift/Sources/wordpress-api/WordPressAPI.swift +++ b/native/swift/Sources/wordpress-api/WordPressAPI.swift @@ -153,6 +153,10 @@ public actor WordPressAPI { self.requestBuilder.wpBlockEditor() } + public var navigations: NavigationRequestExecutor { + self.requestBuilder.navigations() + } + public var navMenus: NavMenusRequestExecutor { self.requestBuilder.navMenus() } diff --git a/scripts/setup-test-site.sh b/scripts/setup-test-site.sh index 45f4a3282..b5950b451 100755 --- a/scripts/setup-test-site.sh +++ b/scripts/setup-test-site.sh @@ -237,6 +237,10 @@ create_test_credentials () { NAV_MENU_ITEM_AUTOSAVE_RESPONSE="$(create_nav_menu_item_autosave "1" "$NAV_MENU_ITEM_ID")" AUTOSAVE_ID_FOR_NAV_MENU_ITEM_ID="$(echo "$NAV_MENU_ITEM_AUTOSAVE_RESPONSE" | jq -r '.id')" + echo "Creating a navigation for integration tests.." + NAVIGATION_RESPONSE="$(curl --silent --user "$ADMIN_USERNAME":"$ADMIN_PASSWORD" -H "Content-Type: application/json" -d '{"title":"Integration Test Navigation","content":"","status":"publish"}' http://localhost/wp-json/wp/v2/navigation)" + NAVIGATION_ID="$(echo "$NAVIGATION_RESPONSE" | jq -r '.id')" + rm -rf /app/test_credentials.json jo -p \ site_url="$SITE_URL" \ @@ -274,6 +278,7 @@ create_test_credentials () { footer_menu_location="$FOOTER_MENU_LOCATION" \ nav_menu_item_id="$NAV_MENU_ITEM_ID" \ autosave_id_for_nav_menu_item_id="$AUTOSAVE_ID_FOR_NAV_MENU_ITEM_ID" \ + navigation_id="$NAVIGATION_ID" \ > /app/test_credentials.json } create_test_credentials diff --git a/wp_api/src/api_client.rs b/wp_api/src/api_client.rs index a62b5790f..dfd553096 100644 --- a/wp_api/src/api_client.rs +++ b/wp_api/src/api_client.rs @@ -19,6 +19,7 @@ use crate::{ }, nav_menu_items_endpoint::{NavMenuItemsRequestBuilder, NavMenuItemsRequestExecutor}, nav_menus_endpoint::{NavMenusRequestBuilder, NavMenusRequestExecutor}, + navigations_endpoint::{NavigationsRequestBuilder, NavigationsRequestExecutor}, plugins_endpoint::{PluginsRequestBuilder, PluginsRequestExecutor}, post_autosaves_endpoint::{AutosavesRequestBuilder, AutosavesRequestExecutor}, post_revisions_endpoint::{PostRevisionsRequestBuilder, PostRevisionsRequestExecutor}, @@ -58,6 +59,7 @@ pub struct WpApiRequestBuilder { nav_menu_item_autosaves: Arc, nav_menu_items: Arc, nav_menus: Arc, + navigations: Arc, plugins: Arc, post_revisions: Arc, post_statuses: Arc, @@ -93,6 +95,7 @@ impl WpApiRequestBuilder { nav_menu_item_autosaves, nav_menu_items, nav_menus, + navigations, plugins, post_revisions, post_statuses, @@ -138,6 +141,7 @@ pub struct WpApiClient { nav_menu_item_autosaves: Arc, nav_menu_items: Arc, nav_menus: Arc, + navigations: Arc, plugins: Arc, post_revisions: Arc, post_statuses: Arc, @@ -170,6 +174,7 @@ impl WpApiClient { nav_menu_item_autosaves, nav_menu_items, nav_menus, + navigations, plugins, post_revisions, post_statuses, @@ -212,6 +217,7 @@ api_client_generate_endpoint_impl!(WpApi, menu_locations); api_client_generate_endpoint_impl!(WpApi, nav_menu_item_autosaves); api_client_generate_endpoint_impl!(WpApi, nav_menu_items); api_client_generate_endpoint_impl!(WpApi, nav_menus); +api_client_generate_endpoint_impl!(WpApi, navigations); api_client_generate_endpoint_impl!(WpApi, plugins); api_client_generate_endpoint_impl!(WpApi, post_revisions); api_client_generate_endpoint_impl!(WpApi, post_statuses); diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 3a27a84ff..88e238b73 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -22,6 +22,7 @@ pub mod middleware; pub mod nav_menu_item_revisions; pub mod nav_menu_items; pub mod nav_menus; +pub mod navigations; pub mod parsed_url; pub mod plugins; pub mod post_revisions; diff --git a/wp_api/src/navigations.rs b/wp_api/src/navigations.rs new file mode 100644 index 000000000..15ac48d97 --- /dev/null +++ b/wp_api/src/navigations.rs @@ -0,0 +1,320 @@ +use crate::{ + WpApiParamOrder, impl_as_query_value_from_to_string, + url_query::{ + AppendUrlQueryPairs, FromUrlQueryPairs, QueryPairs, QueryPairsExtension, UrlQueryPairsMap, + }, + wp_content_i64_id, +}; +use serde::{Deserialize, Serialize}; +use wp_contextual::WpContextual; +use wp_derive::WpDeriveParamsField; + +wp_content_i64_id!(NavigationId); + +#[derive( + Debug, + Default, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + uniffi::Enum, + strum_macros::EnumString, + strum_macros::Display, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum NavigationStatus { + Draft, + Future, + Pending, + Private, + #[default] + Publish, + #[serde(untagged)] + #[strum(default)] + Custom(String), +} + +impl_as_query_value_from_to_string!(NavigationStatus); + +#[derive(Debug, Serialize, Deserialize, WpContextual)] +pub struct SparseNavigation { + #[WpContext(edit, embed, view)] + pub date: Option, + #[WpContext(edit, view)] + pub date_gmt: Option, + #[WpContext(edit, view)] + pub guid: Option, + #[WpContext(edit, embed, view)] + pub id: Option, + #[WpContext(edit, embed, view)] + pub link: Option, + #[WpContext(edit, view)] + pub modified: Option, + #[WpContext(edit, view)] + pub modified_gmt: Option, + #[WpContext(edit, embed, view)] + pub slug: Option, + #[WpContext(edit, embed, view)] + pub status: Option, + #[WpContext(edit, embed, view)] + #[serde(rename = "type")] + pub navigation_type: Option, + #[WpContext(edit)] + #[WpContextualOption] + pub password: Option, + #[WpContext(edit, embed, view)] + pub title: Option, + #[WpContext(edit, embed, view)] + pub content: Option, + #[WpContext(edit, view)] + #[WpContextualOption] + pub template: Option, + // _meta field omitted +} + +#[derive(Debug, Serialize, PartialEq, PartialOrd, Eq, wp_derive::WpDeserialize, uniffi::Record)] +pub struct SparseNavigationGuid { + pub rendered: Option, + pub raw: Option, +} + +#[derive(Debug, Serialize, PartialEq, PartialOrd, Eq, Deserialize, uniffi::Enum)] +#[serde(untagged)] +pub enum SparseNavigationTitleWrapper { + Object(SparseNavigationTitle), + String(String), +} + +#[derive(Debug, Serialize, PartialEq, PartialOrd, Eq, wp_derive::WpDeserialize, uniffi::Record)] +pub struct SparseNavigationTitle { + pub raw: Option, + pub rendered: Option, +} + +#[derive(Debug, Serialize, PartialEq, PartialOrd, Eq, Deserialize, uniffi::Enum)] +#[serde(untagged)] +pub enum SparseNavigationContentWrapper { + Object(SparseNavigationContent), + String(String), +} + +#[derive(Debug, Serialize, PartialEq, PartialOrd, Eq, wp_derive::WpDeserialize, uniffi::Record)] +pub struct SparseNavigationContent { + pub raw: Option, + pub rendered: Option, + pub protected: Option, + pub block_version: Option, +} + +#[derive(Debug, Default, PartialEq, Eq, uniffi::Record, WpDeriveParamsField)] +#[supports_pagination(true)] +pub struct NavigationListParams { + /// Current page of the collection. + #[uniffi(default = None)] + pub page: Option, + /// Maximum number of items to be returned in result set. + #[uniffi(default = None)] + pub per_page: Option, + /// Limit results to those matching a string. + #[uniffi(default = None)] + pub search: Option, + /// Limit response to posts published after a given ISO8601 compliant date. + #[uniffi(default = None)] + pub after: Option, + /// Limit response to posts modified after a given ISO8601 compliant date. + #[uniffi(default = None)] + pub modified_after: Option, + /// Limit response to posts published before a given ISO8601 compliant date. + #[uniffi(default = None)] + pub before: Option, + /// Limit response to posts modified before a given ISO8601 compliant date. + #[uniffi(default = None)] + pub modified_before: Option, + /// Ensure result set excludes specific IDs. + #[uniffi(default = [])] + pub exclude: Vec, + /// Limit result set to specific IDs. + #[uniffi(default = [])] + pub include: Vec, + /// Offset the result set by a specific number of items. + #[uniffi(default = None)] + pub offset: Option, + /// Order sort attribute ascending or descending. + #[uniffi(default = None)] + pub order: Option, + /// Sort collection by post attribute. + #[uniffi(default = None)] + #[field_name("orderby")] + pub order_by: Option, + /// Array of column names to be searched. + #[uniffi(default = [])] + pub search_columns: Vec, + /// Limit result set to posts with one or more specific slugs. + #[uniffi(default = [])] + pub slug: Vec, + /// Limit result set to posts assigned one or more statuses. + #[uniffi(default = [])] + pub status: Vec, +} + +#[derive( + Debug, + Default, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Serialize, + Deserialize, + uniffi::Enum, + strum_macros::EnumString, + strum_macros::Display, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum NavigationOrderBy { + Author, + #[default] + Date, + Id, + Include, + Modified, + Parent, + Relevance, + Slug, + IncludeSlugs, + Title, +} + +impl_as_query_value_from_to_string!(NavigationOrderBy); + +#[derive(Debug, Default, PartialEq, Eq, uniffi::Record, WpDeriveParamsField)] +#[supports_pagination(false)] +pub struct NavigationRetrieveParams { + /// The password for the post if it is password protected. + #[uniffi(default = None)] + pub password: Option, +} + +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] +pub struct NavigationDeleteResponse { + pub deleted: bool, + pub previous: NavigationWithEditContext, +} + +#[derive(Debug, Default, Serialize, uniffi::Record)] +pub struct NavigationUpdateParams { + /// The date the post was published, in the site's timezone. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub date: Option, + /// The date the post was published, as GMT. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub date_gmt: Option, + /// An alphanumeric identifier for the post unique to its type. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub slug: Option, + /// A named status for the post. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, + /// A password to protect access to the content and excerpt. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub password: Option, + /// The title for the post. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + /// The content for the post. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub content: Option, + /// The theme file to use to display the post. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub template: Option, +} + +#[derive(Debug, Default, Serialize, uniffi::Record)] +pub struct NavigationCreateParams { + /// The date the post was published, in the site's timezone. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub date: Option, + /// The date the post was published, as GMT. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub date_gmt: Option, + /// An alphanumeric identifier for the post unique to its type. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub slug: Option, + /// A named status for the post. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, + /// A password to protect access to the content and excerpt. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub password: Option, + /// The title for the post. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + /// The content for the post. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub content: Option, + /// The theme file to use to display the post. + #[uniffi(default = None)] + #[serde(skip_serializing_if = "Option::is_none")] + pub template: Option, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::SparseField; + use rstest::*; + + #[rstest] + #[case(SparseNavigationFieldWithEditContext::Id, "id")] + #[case(SparseNavigationFieldWithEditContext::NavigationType, "type")] + fn test_as_mapped_field_name_for_edit_context( + #[case] field: SparseNavigationFieldWithEditContext, + #[case] expected_mapped_field_name: &str, + ) { + assert_eq!(field.as_mapped_field_name(), expected_mapped_field_name); + } + + #[rstest] + #[case(SparseNavigationFieldWithEmbedContext::Id, "id")] + #[case(SparseNavigationFieldWithEmbedContext::NavigationType, "type")] + fn test_as_mapped_field_name_for_embed_context( + #[case] field: SparseNavigationFieldWithEmbedContext, + #[case] expected_mapped_field_name: &str, + ) { + assert_eq!(field.as_mapped_field_name(), expected_mapped_field_name); + } + + #[rstest] + #[case(SparseNavigationFieldWithViewContext::Id, "id")] + #[case(SparseNavigationFieldWithViewContext::NavigationType, "type")] + fn test_as_mapped_field_name_for_view_context( + #[case] field: SparseNavigationFieldWithViewContext, + #[case] expected_mapped_field_name: &str, + ) { + assert_eq!(field.as_mapped_field_name(), expected_mapped_field_name); + } +} diff --git a/wp_api/src/post_revisions.rs b/wp_api/src/post_revisions.rs index 89d70b464..418025d3f 100644 --- a/wp_api/src/post_revisions.rs +++ b/wp_api/src/post_revisions.rs @@ -102,7 +102,7 @@ pub struct SparseAnyPostRevision { #[WpContextualField] pub content: Option, #[WpContext(edit, embed, view)] - #[WpContextualField] + #[WpContextualOption] pub excerpt: Option, #[WpContext(edit, view)] pub meta: Option, diff --git a/wp_api/src/posts.rs b/wp_api/src/posts.rs index 927f0ae69..e2a888e13 100644 --- a/wp_api/src/posts.rs +++ b/wp_api/src/posts.rs @@ -382,8 +382,10 @@ pub struct SparseAnyPost { #[WpContext(edit)] pub password: Option, #[WpContext(edit)] + #[WpContextualOption] pub permalink_template: Option, #[WpContext(edit)] + #[WpContextualOption] pub generated_slug: Option, #[WpContext(edit, embed, view)] #[WpContextualField] @@ -395,13 +397,16 @@ pub struct SparseAnyPost { #[WpContextualOption] pub author: Option, #[WpContext(edit, embed, view)] - #[WpContextualField] + #[WpContextualOption] pub excerpt: Option, #[WpContext(edit, embed, view)] + #[WpContextualOption] pub featured_media: Option, #[WpContext(edit, view)] + #[WpContextualOption] pub comment_status: Option, #[WpContext(edit, view)] + #[WpContextualOption] pub ping_status: Option, #[WpContext(edit, view)] #[WpContextualOption] @@ -462,15 +467,10 @@ pub struct SparsePostContent { pub block_version: Option, } -#[derive(Debug, Serialize, Deserialize, uniffi::Record, WpContextual)] +#[derive(Debug, Serialize, Deserialize, uniffi::Record)] pub struct SparsePostExcerpt { - #[WpContext(edit)] - #[WpContextualOption] pub raw: Option, - #[WpContext(edit, embed, view)] pub rendered: Option, - #[WpContext(edit, embed, view)] - #[WpContextualOption] pub protected: Option, } diff --git a/wp_api/src/request/endpoint.rs b/wp_api/src/request/endpoint.rs index df858ddd4..62cd1a954 100644 --- a/wp_api/src/request/endpoint.rs +++ b/wp_api/src/request/endpoint.rs @@ -11,6 +11,7 @@ pub mod menu_locations_endpoint; pub mod nav_menu_item_autosaves_endpoint; pub mod nav_menu_items_endpoint; pub mod nav_menus_endpoint; +pub mod navigations_endpoint; pub mod plugins_endpoint; pub mod post_autosaves_endpoint; pub mod post_revisions_endpoint; diff --git a/wp_api/src/request/endpoint/navigations_endpoint.rs b/wp_api/src/request/endpoint/navigations_endpoint.rs new file mode 100644 index 000000000..9ec29f1da --- /dev/null +++ b/wp_api/src/request/endpoint/navigations_endpoint.rs @@ -0,0 +1,35 @@ +use super::{AsNamespace, DerivedRequest, WpNamespace}; +use crate::navigations::{ + NavigationId, NavigationListParams, NavigationUpdateParams, NavigationWithEditContext, +}; +use wp_derive_request_builder::WpDerivedRequest; + +#[derive(WpDerivedRequest)] +enum NavigationsRequest { + #[contextual_paged(url = "/navigation", params = &NavigationListParams, output = Vec, filter_by = crate::navigations::SparseNavigationField)] + List, + #[contextual_get(url = "/navigation/", params = &crate::navigations::NavigationRetrieveParams, output = crate::navigations::SparseNavigation, filter_by = crate::navigations::SparseNavigationField)] + Retrieve, + #[post(url = "/navigation", params = &crate::navigations::NavigationCreateParams, output = crate::navigations::NavigationWithEditContext)] + Create, + #[delete(url = "/navigation/", output = crate::navigations::NavigationDeleteResponse)] + Delete, + #[delete(url = "/navigation/", output = crate::navigations::NavigationWithEditContext)] + Trash, + #[post(url = "/navigation/", params = &NavigationUpdateParams, output = NavigationWithEditContext)] + Update, +} + +impl DerivedRequest for NavigationsRequest { + fn additional_query_pairs(&self) -> Vec<(&str, String)> { + match self { + NavigationsRequest::Delete => vec![("force", true.to_string())], + NavigationsRequest::Trash => vec![("force", false.to_string())], + _ => vec![], + } + } + + fn namespace() -> impl AsNamespace { + WpNamespace::WpV2 + } +} diff --git a/wp_api/src/request/endpoint/posts_endpoint.rs b/wp_api/src/request/endpoint/posts_endpoint.rs index bdedcb7f8..dd68fad4a 100644 --- a/wp_api/src/request/endpoint/posts_endpoint.rs +++ b/wp_api/src/request/endpoint/posts_endpoint.rs @@ -34,6 +34,7 @@ enum PostsRequest { pub enum PostEndpointType { Posts, Pages, + Navigation, #[strum(default)] Custom(String), } diff --git a/wp_api_integration_tests/src/lib.rs b/wp_api_integration_tests/src/lib.rs index 0d7849b92..bcaaf78c7 100644 --- a/wp_api_integration_tests/src/lib.rs +++ b/wp_api_integration_tests/src/lib.rs @@ -47,6 +47,7 @@ pub struct TestCredentials { pub footer_menu_location: &'static str, pub nav_menu_item_id: i64, pub autosave_id_for_nav_menu_item_id: i64, + pub navigation_id: i64, } impl TestCredentials { diff --git a/wp_api_integration_tests/tests/test_navigations_err.rs b/wp_api_integration_tests/tests/test_navigations_err.rs new file mode 100644 index 000000000..0b22c8e3c --- /dev/null +++ b/wp_api_integration_tests/tests/test_navigations_err.rs @@ -0,0 +1,159 @@ +use wp_api::{ + posts::{ + PostCreateParams, PostId, PostListParams, PostRetrieveParams, PostUpdateParams, + WpApiParamPostsOrderBy, + }, + request::endpoint::posts_endpoint::PostEndpointType, +}; +use wp_api_integration_tests::prelude::*; + +#[tokio::test] +#[parallel] +async fn create_navigation_err_cannot_create() { + api_client_as_subscriber() + .posts() + .create( + &PostEndpointType::Navigation, + &PostCreateParams { + ..Default::default() + }, + ) + .await + .assert_wp_error(WpErrorCode::CannotCreate); +} + +#[tokio::test] +#[parallel] +async fn create_navigation_err_cannot_create2() { + api_client_as_subscriber() + .posts() + .create( + &PostEndpointType::Navigation, + &PostCreateParams { + title: Some("foo".to_string()), + ..Default::default() + }, + ) + .await + .assert_wp_error(WpErrorCode::CannotCreate); +} + +#[tokio::test] +#[parallel] +async fn delete_navigation_err_cannot_delete() { + api_client_as_subscriber() + .posts() + .delete( + &PostEndpointType::Navigation, + &PostId(TestCredentials::instance().navigation_id), + ) + .await + .assert_wp_error(WpErrorCode::CannotDelete); +} + +#[tokio::test] +#[parallel] +async fn list_err_no_search_term_defined() { + api_client() + .posts() + .list_with_edit_context( + &PostEndpointType::Navigation, + &PostListParams { + orderby: Some(WpApiParamPostsOrderBy::Relevance), + ..Default::default() + }, + ) + .await + .assert_wp_error(WpErrorCode::NoSearchTermDefined); +} + +#[tokio::test] +#[parallel] +async fn list_err_order_by_include_missing_include() { + api_client() + .posts() + .list_with_edit_context( + &PostEndpointType::Navigation, + &PostListParams { + orderby: Some(WpApiParamPostsOrderBy::Include), + ..Default::default() + }, + ) + .await + .assert_wp_error(WpErrorCode::OrderbyIncludeMissingInclude); +} + +#[tokio::test] +#[parallel] +async fn list_err_post_invalid_page_number() { + api_client() + .posts() + .list_with_edit_context( + &PostEndpointType::Navigation, + &PostListParams { + page: Some(99999999), + ..Default::default() + }, + ) + .await + .assert_wp_error(WpErrorCode::PostInvalidPageNumber); +} + +#[tokio::test] +#[parallel] +async fn retrieve_navigation_err_forbidden_context() { + api_client_as_subscriber() + .posts() + .retrieve_with_edit_context( + &PostEndpointType::Navigation, + &PostId(TestCredentials::instance().navigation_id), + &PostRetrieveParams::default(), + ) + .await + .assert_wp_error(WpErrorCode::ForbiddenContext); +} + +#[tokio::test] +#[parallel] +async fn retrieve_navigation_err_post_invalid_id() { + api_client() + .posts() + .retrieve_with_edit_context( + &PostEndpointType::Navigation, + &PostId(99999999), + &PostRetrieveParams::default(), + ) + .await + .assert_wp_error(WpErrorCode::PostInvalidId); +} + +#[tokio::test] +#[parallel] +async fn update_navigation_err_cannot_edit() { + api_client_as_author() + .posts() + .update( + &PostEndpointType::Navigation, + &PostId(TestCredentials::instance().navigation_id), + &PostUpdateParams::default(), + ) + .await + .assert_wp_error(WpErrorCode::CannotEdit); +} + +#[tokio::test] +#[parallel] +async fn update_navigation_err_invalid_template() { + api_client() + .posts() + .update( + &PostEndpointType::Navigation, + &PostId(TestCredentials::instance().navigation_id), + &PostUpdateParams { + template: Some("foo".to_string()), + ..Default::default() + }, + ) + .await + .assert_wp_error(WpErrorCode::InvalidParam); +} diff --git a/wp_api_integration_tests/tests/test_navigations_immut.rs b/wp_api_integration_tests/tests/test_navigations_immut.rs new file mode 100644 index 000000000..a5ec161dd --- /dev/null +++ b/wp_api_integration_tests/tests/test_navigations_immut.rs @@ -0,0 +1,283 @@ +use wp_api::{ + posts::{ + PostListParams, PostRetrieveParams, PostStatus, SparseAnyPostFieldWithEditContext, + SparseAnyPostFieldWithEmbedContext, SparseAnyPostFieldWithViewContext, + WpApiParamPostsOrderBy, WpApiParamPostsSearchColumn, + }, + request::endpoint::posts_endpoint::PostEndpointType, +}; +use wp_api_integration_tests::prelude::*; + +#[tokio::test] +#[apply(list_cases)] +#[parallel] +async fn list_with_edit_context(#[case] params: PostListParams) { + api_client() + .posts() + .list_with_edit_context(&PostEndpointType::Navigation, ¶ms) + .await + .assert_response(); +} + +#[tokio::test] +#[apply(list_cases)] +#[parallel] +async fn list_with_embed_context(#[case] params: PostListParams) { + api_client() + .posts() + .list_with_embed_context(&PostEndpointType::Navigation, ¶ms) + .await + .assert_response(); +} + +#[tokio::test] +#[apply(list_cases)] +#[parallel] +async fn list_with_view_context(#[case] params: PostListParams) { + api_client() + .posts() + .list_with_view_context(&PostEndpointType::Navigation, ¶ms) + .await + .assert_response(); +} + +#[tokio::test] +#[parallel] +async fn retrieve_with_edit_context() { + api_client() + .posts() + .retrieve_with_edit_context( + &PostEndpointType::Navigation, + &navigation_id(), + &PostRetrieveParams::default(), + ) + .await + .assert_response(); +} + +#[tokio::test] +#[parallel] +async fn retrieve_with_embed_context() { + api_client() + .posts() + .retrieve_with_embed_context( + &PostEndpointType::Navigation, + &navigation_id(), + &PostRetrieveParams::default(), + ) + .await + .assert_response(); +} + +#[tokio::test] +#[parallel] +async fn retrieve_with_view_context() { + api_client() + .posts() + .retrieve_with_view_context( + &PostEndpointType::Navigation, + &navigation_id(), + &PostRetrieveParams::default(), + ) + .await + .assert_response(); +} + +#[tokio::test] +#[rstest] +#[parallel] +#[case(PostListParams { per_page: Some(1), ..Default::default() })] +#[case(PostListParams { per_page: Some(1), order: Some(WpApiParamOrder::Desc), ..Default::default() })] +#[case(PostListParams { per_page: Some(1), orderby: Some(WpApiParamPostsOrderBy::Modified), ..Default::default() })] +async fn paginate_list_navigations_with_edit_context(#[case] params: PostListParams) { + let first_page_response = api_client() + .posts() + .list_with_edit_context(&PostEndpointType::Navigation, ¶ms) + .await + .assert_response(); + assert!(!first_page_response.data.is_empty()); + let next_page_params = first_page_response.next_page_params.unwrap(); + let next_page_response = api_client() + .posts() + .list_with_edit_context(&PostEndpointType::Navigation, &next_page_params) + .await + .assert_response(); + assert!(!next_page_response.data.is_empty()); + let prev_page_params = next_page_response.prev_page_params.unwrap(); + let prev_page_response = api_client() + .posts() + .list_with_edit_context(&PostEndpointType::Navigation, &prev_page_params) + .await + .assert_response(); + assert!(!prev_page_response.data.is_empty()); +} + +fn navigation_id() -> PostId { + PostId(TestCredentials::instance().navigation_id) +} + +#[template] +#[rstest] +#[case::default(PostListParams::default())] +#[case::page(generate!(PostListParams, (page, Some(1))))] +#[case::per_page(generate!(PostListParams, (per_page, Some(3))))] +#[case::search(generate!(PostListParams, (search, Some("foo".to_string()))))] +#[case::after(generate!(PostListParams, (after, Some(unwrapped_wp_gmt_date_time("2020-08-14T17:00:00+0200")))))] +#[case::modified_after(generate!(PostListParams, (modified_after, Some(unwrapped_wp_gmt_date_time("2024-01-14T17:00:00+0200")))))] +#[case::before(generate!(PostListParams, (before, Some(unwrapped_wp_gmt_date_time("2023-08-14T17:00:00+0000")))))] +#[case::modified_before(generate!(PostListParams, (modified_before, Some(unwrapped_wp_gmt_date_time("2024-01-14T17:00:00+0000")))))] +#[case::exclude(generate!(PostListParams, (exclude, vec![PostId(1), PostId(2)])))] +#[case::include(generate!(PostListParams, (include, vec![PostId(1)])))] +#[case::offset(generate!(PostListParams, (offset, Some(2))))] +#[case::order(generate!(PostListParams, (order, Some(WpApiParamOrder::Asc))))] +#[case::orderby(generate!(PostListParams, (orderby, Some(WpApiParamPostsOrderBy::Id))))] +#[case::search_columns(generate!(PostListParams, (search_columns, vec![WpApiParamPostsSearchColumn::PostContent, WpApiParamPostsSearchColumn::PostExcerpt])))] +#[case::slug(generate!(PostListParams, (slug, vec!["foo".to_string(), "bar".to_string()])))] +#[case::status(generate!(PostListParams, (status, vec![PostStatus::Publish, PostStatus::Draft])))] +fn list_cases(#[case] params: PostListParams) {} + +mod filter { + use super::*; + + wp_api::generate_sparse_any_post_field_with_edit_context_test_cases!(); + wp_api::generate_sparse_any_post_field_with_embed_context_test_cases!(); + wp_api::generate_sparse_any_post_field_with_view_context_test_cases!(); + + #[apply(sparse_any_post_field_with_edit_context_test_cases)] + #[case(&[SparseAnyPostFieldWithEditContext::Id, SparseAnyPostFieldWithEditContext::Slug])] + #[tokio::test] + #[parallel] + async fn filter_navigations_with_edit_context( + #[case] fields: &[SparseAnyPostFieldWithEditContext], + #[values( + PostListParams::default(), + generate!(PostListParams, (status, vec![PostStatus::Draft, PostStatus::Publish])), + generate!(PostListParams, (search, Some("foo".to_string()))) + )] + params: PostListParams, + ) { + api_client() + .posts() + .filter_list_with_edit_context(&PostEndpointType::Navigation, ¶ms, fields) + .await + .assert_response() + .data + .iter() + .for_each(|navigation| { + navigation.assert_that_instance_fields_nullability_match_provided_fields(fields) + }); + } + + #[apply(sparse_any_post_field_with_edit_context_test_cases)] + #[case(&[SparseAnyPostFieldWithEditContext::Id, SparseAnyPostFieldWithEditContext::Slug])] + #[tokio::test] + #[parallel] + async fn filter_retrieve_navigations_with_edit_context( + #[case] fields: &[SparseAnyPostFieldWithEditContext], + ) { + let navigation = api_client() + .posts() + .filter_retrieve_with_edit_context( + &PostEndpointType::Navigation, + &navigation_id(), + &PostRetrieveParams::default(), + fields, + ) + .await + .assert_response() + .data; + navigation.assert_that_instance_fields_nullability_match_provided_fields(fields) + } + + #[apply(sparse_any_post_field_with_embed_context_test_cases)] + #[case(&[SparseAnyPostFieldWithEmbedContext::Id, SparseAnyPostFieldWithEmbedContext::Slug])] + #[tokio::test] + #[parallel] + async fn filter_navigations_with_embed_context( + #[case] fields: &[SparseAnyPostFieldWithEmbedContext], + #[values( + PostListParams::default(), + generate!(PostListParams, (status, vec![PostStatus::Draft, PostStatus::Publish])), + generate!(PostListParams, (search, Some("foo".to_string()))) + )] + params: PostListParams, + ) { + api_client() + .posts() + .filter_list_with_embed_context(&PostEndpointType::Navigation, ¶ms, fields) + .await + .assert_response() + .data + .iter() + .for_each(|navigation| { + navigation.assert_that_instance_fields_nullability_match_provided_fields(fields) + }); + } + + #[apply(sparse_any_post_field_with_embed_context_test_cases)] + #[case(&[SparseAnyPostFieldWithEmbedContext::Id, SparseAnyPostFieldWithEmbedContext::Slug])] + #[tokio::test] + #[parallel] + async fn filter_retrieve_navigations_with_embed_context( + #[case] fields: &[SparseAnyPostFieldWithEmbedContext], + ) { + let navigation = api_client() + .posts() + .filter_retrieve_with_embed_context( + &PostEndpointType::Navigation, + &navigation_id(), + &PostRetrieveParams::default(), + fields, + ) + .await + .assert_response() + .data; + navigation.assert_that_instance_fields_nullability_match_provided_fields(fields) + } + + #[apply(sparse_any_post_field_with_view_context_test_cases)] + #[case(&[SparseAnyPostFieldWithViewContext::Id, SparseAnyPostFieldWithViewContext::Slug])] + #[tokio::test] + #[parallel] + async fn filter_navigations_with_view_context( + #[case] fields: &[SparseAnyPostFieldWithViewContext], + #[values( + PostListParams::default(), + generate!(PostListParams, (status, vec![PostStatus::Draft, PostStatus::Publish])), + generate!(PostListParams, (search, Some("foo".to_string()))) + )] + params: PostListParams, + ) { + api_client() + .posts() + .filter_list_with_view_context(&PostEndpointType::Navigation, ¶ms, fields) + .await + .assert_response() + .data + .iter() + .for_each(|navigation| { + navigation.assert_that_instance_fields_nullability_match_provided_fields(fields) + }); + } + + #[apply(sparse_any_post_field_with_view_context_test_cases)] + #[case(&[SparseAnyPostFieldWithViewContext::Id, SparseAnyPostFieldWithViewContext::Slug])] + #[tokio::test] + #[parallel] + async fn filter_retrieve_navigations_with_view_context( + #[case] fields: &[SparseAnyPostFieldWithViewContext], + ) { + let navigation = api_client() + .posts() + .filter_retrieve_with_view_context( + &PostEndpointType::Navigation, + &navigation_id(), + &PostRetrieveParams::default(), + fields, + ) + .await + .assert_response() + .data; + navigation.assert_that_instance_fields_nullability_match_provided_fields(fields) + } +} diff --git a/wp_api_integration_tests/tests/test_navigations_mut.rs b/wp_api_integration_tests/tests/test_navigations_mut.rs new file mode 100644 index 000000000..5e8bd450b --- /dev/null +++ b/wp_api_integration_tests/tests/test_navigations_mut.rs @@ -0,0 +1,244 @@ +use macro_helper::{generate_update_navigation_status_test, generate_update_test}; +use wp_api::posts::{AnyPostWithEditContext, PostCreateParams, PostStatus, PostUpdateParams}; +use wp_api::request::endpoint::posts_endpoint::PostEndpointType; +use wp_api_integration_tests::prelude::*; + +#[tokio::test] +#[serial] +async fn create_navigation_with_just_title() { + test_create_navigation( + &PostCreateParams { + title: Some("foo".to_string()), + ..Default::default() + }, + |created_navigation| { + assert_eq!(created_navigation.title.raw, Some("foo".to_string())); + }, + ) + .await; +} + +#[tokio::test] +#[serial] +async fn create_navigation_with_just_content() { + test_create_navigation( + &PostCreateParams { + content: Some("foo".to_string()), + ..Default::default() + }, + |created_navigation| { + assert_eq!(created_navigation.content.raw, Some("foo".to_string())); + }, + ) + .await; +} + +#[tokio::test] +#[serial] +async fn create_navigation_with_title_and_content() { + test_create_navigation( + &PostCreateParams { + title: Some("foo".to_string()), + content: Some("bar".to_string()), + ..Default::default() + }, + |created_navigation| { + assert_eq!(created_navigation.title.raw, Some("foo".to_string())); + assert_eq!(created_navigation.content.raw, Some("bar".to_string())); + }, + ) + .await; +} + +#[tokio::test] +#[serial] +async fn delete_navigation() { + let navigation_delete_response = api_client() + .posts() + .delete( + &PostEndpointType::Navigation, + &PostId(TestCredentials::instance().navigation_id), + ) + .await; + assert!( + navigation_delete_response.is_ok(), + "{navigation_delete_response:#?}" + ); + assert!(navigation_delete_response.unwrap().data.deleted); + + RestoreServer::db().await; +} + +#[tokio::test] +#[serial] +async fn trash_navigation() { + let navigation_trash_response = api_client() + .posts() + .trash( + &PostEndpointType::Navigation, + &PostId(TestCredentials::instance().navigation_id), + ) + .await; + assert!( + navigation_trash_response.is_ok(), + "{navigation_trash_response:#?}" + ); + + RestoreServer::db().await; +} + +generate_update_test!( + update_date, + date, + "2024-09-09T12:00:00".to_string(), + |updated_navigation| { + assert_eq!(updated_navigation.date, "2024-09-09T12:00:00"); + } +); + +generate_update_test!( + update_date_gmt, + date_gmt, + unwrapped_wp_gmt_date_time("2024-09-09T12:00:00+0000"), + |updated_navigation| { + assert_eq!( + updated_navigation.date_gmt, + unwrapped_wp_gmt_date_time("2024-09-09T12:00:00+0000") + ); + } +); + +generate_update_test!( + update_slug, + slug, + "new_slug".to_string(), + |updated_navigation| { + assert_eq!(updated_navigation.slug, "new_slug"); + } +); + +generate_update_test!( + update_password, + password, + "new_password".to_string(), + |updated_navigation| { + assert_eq!(updated_navigation.password, "new_password"); + } +); + +generate_update_test!( + update_title, + title, + "new_title".to_string(), + |updated_navigation| { + assert_eq!(updated_navigation.title.raw, Some("new_title".to_string())); + } +); + +generate_update_test!( + update_content, + content, + "new_content".to_string(), + |updated_navigation| { + assert_eq!( + updated_navigation.content.raw, + Some("new_content".to_string()) + ); + } +); + +#[tokio::test] +#[serial] +async fn update_status_to_future() { + test_update_navigation( + &PostUpdateParams { + status: Some(PostStatus::Future), + date: Some("2026-09-09T12:00:00".to_string()), + ..Default::default() + }, + |updated_navigation| { + assert_eq!(updated_navigation.status, PostStatus::Future); + }, + ) + .await; +} + +generate_update_navigation_status_test!(Draft); +generate_update_navigation_status_test!(Pending); +generate_update_navigation_status_test!(Private); +generate_update_navigation_status_test!(Publish); + +async fn test_create_navigation(params: &PostCreateParams, assert: F) +where + F: Fn(AnyPostWithEditContext), +{ + let created_navigation = api_client() + .posts() + .create(&PostEndpointType::Navigation, params) + .await + .assert_response() + .data; + assert(created_navigation); + RestoreServer::db().await; +} + +async fn test_update_navigation(params: &PostUpdateParams, assert: F) +where + F: Fn(AnyPostWithEditContext), +{ + let updated_navigation = api_client() + .posts() + .update( + &PostEndpointType::Navigation, + &PostId(TestCredentials::instance().navigation_id), + params, + ) + .await + .assert_response() + .data; + assert(updated_navigation); + RestoreServer::db().await; +} + +mod macro_helper { + macro_rules! generate_update_test { + ($ident:ident, $field:ident, $new_value:expr, $assertion:expr) => { + paste::paste! { + #[tokio::test] + #[serial] + async fn $ident() { + let updated_value = $new_value; + test_update_navigation( + &PostUpdateParams { + $field: Some(updated_value), + ..Default::default() + }, $assertion) + .await; + } + } + }; + } + + macro_rules! generate_update_navigation_status_test { + ($status:ident) => { + paste::paste! { + #[tokio::test] + #[serial] + async fn []() { + test_update_navigation( + &PostUpdateParams { + status: Some(PostStatus::$status), + ..Default::default() + }, + |updated_navigation| { + assert_eq!(updated_navigation.status, PostStatus::$status); + } + ).await; + } + } + }; + } + + pub(super) use generate_update_navigation_status_test; + pub(super) use generate_update_test; +} diff --git a/wp_api_integration_tests/tests/test_pages_mut.rs b/wp_api_integration_tests/tests/test_pages_mut.rs index 4f79cbfbf..43dc12f73 100644 --- a/wp_api_integration_tests/tests/test_pages_mut.rs +++ b/wp_api_integration_tests/tests/test_pages_mut.rs @@ -74,7 +74,7 @@ async fn create_page_with_just_excerpt() { ..Default::default() }, |created_page, page_from_wp_cli| { - assert_eq!(created_page.excerpt.raw, Some("foo".to_string())); + assert_eq!(created_page.excerpt.unwrap().raw, Some("foo".to_string())); assert_eq!(page_from_wp_cli.excerpt, "foo"); }, ) @@ -96,7 +96,7 @@ async fn create_page_with_title_content_and_excerpt() { assert_eq!(page_from_wp_cli.title, "foo"); assert_eq!(created_page.content.raw, Some("bar".to_string())); assert_eq!(page_from_wp_cli.content, "bar"); - assert_eq!(created_page.excerpt.raw, Some("baz".to_string())); + assert_eq!(created_page.excerpt.unwrap().raw, Some("baz".to_string())); assert_eq!(page_from_wp_cli.excerpt, "baz"); }, ) @@ -235,7 +235,10 @@ generate_update_test!( excerpt, "new_excerpt".to_string(), |updated_page, updated_page_from_wp_cli| { - assert_eq!(updated_page.excerpt.raw, Some("new_excerpt".to_string())); + assert_eq!( + updated_page.excerpt.unwrap().raw, + Some("new_excerpt".to_string()) + ); assert_eq!(updated_page_from_wp_cli.excerpt, "new_excerpt"); } ); @@ -245,7 +248,7 @@ generate_update_test!( featured_media, MEDIA_ID_611, |updated_page, _| { - assert_eq!(updated_page.featured_media, MEDIA_ID_611); + assert_eq!(updated_page.featured_media, Some(MEDIA_ID_611)); } ); @@ -254,7 +257,7 @@ generate_update_test!( comment_status, PostCommentStatus::Open, |updated_page, updated_page_from_wp_cli| { - assert_eq!(updated_page.comment_status, PostCommentStatus::Open); + assert_eq!(updated_page.comment_status, Some(PostCommentStatus::Open)); assert_eq!( updated_page_from_wp_cli.comment_status, PostCommentStatus::Open.to_string() @@ -267,7 +270,7 @@ generate_update_test!( comment_status, PostCommentStatus::Closed, |updated_page, updated_page_from_wp_cli| { - assert_eq!(updated_page.comment_status, PostCommentStatus::Closed); + assert_eq!(updated_page.comment_status, Some(PostCommentStatus::Closed)); assert_eq!( updated_page_from_wp_cli.comment_status, PostCommentStatus::Closed.to_string() @@ -280,7 +283,7 @@ generate_update_test!( ping_status, PostPingStatus::Open, |updated_page, updated_page_from_wp_cli| { - assert_eq!(updated_page.ping_status, PostPingStatus::Open); + assert_eq!(updated_page.ping_status, Some(PostPingStatus::Open)); assert_eq!( updated_page_from_wp_cli.ping_status, PostPingStatus::Open.to_string() @@ -293,7 +296,7 @@ generate_update_test!( ping_status, PostPingStatus::Closed, |updated_page, updated_page_from_wp_cli| { - assert_eq!(updated_page.ping_status, PostPingStatus::Closed); + assert_eq!(updated_page.ping_status, Some(PostPingStatus::Closed)); assert_eq!( updated_page_from_wp_cli.ping_status, PostPingStatus::Closed.to_string() diff --git a/wp_api_integration_tests/tests/test_posts_mut.rs b/wp_api_integration_tests/tests/test_posts_mut.rs index de7ffd4ef..aef378423 100644 --- a/wp_api_integration_tests/tests/test_posts_mut.rs +++ b/wp_api_integration_tests/tests/test_posts_mut.rs @@ -76,7 +76,7 @@ async fn create_post_with_just_excerpt() { ..Default::default() }, |created_post, post_from_wp_cli| { - assert_eq!(created_post.excerpt.raw, Some("foo".to_string())); + assert_eq!(created_post.excerpt.unwrap().raw, Some("foo".to_string())); assert_eq!(post_from_wp_cli.excerpt, "foo"); }, ) @@ -98,7 +98,7 @@ async fn create_post_with_title_content_and_excerpt() { assert_eq!(post_from_wp_cli.title, "foo"); assert_eq!(created_post.content.raw, Some("bar".to_string())); assert_eq!(post_from_wp_cli.content, "bar"); - assert_eq!(created_post.excerpt.raw, Some("baz".to_string())); + assert_eq!(created_post.excerpt.unwrap().raw, Some("baz".to_string())); assert_eq!(post_from_wp_cli.excerpt, "baz"); }, ) @@ -231,7 +231,10 @@ generate_update_test!( excerpt, "new_excerpt".to_string(), |updated_post, updated_post_from_wp_cli| { - assert_eq!(updated_post.excerpt.raw, Some("new_excerpt".to_string())); + assert_eq!( + updated_post.excerpt.unwrap().raw, + Some("new_excerpt".to_string()) + ); assert_eq!(updated_post_from_wp_cli.excerpt, "new_excerpt"); } ); @@ -241,7 +244,7 @@ generate_update_test!( featured_media, MEDIA_ID_611, |updated_post, _| { - assert_eq!(updated_post.featured_media, MEDIA_ID_611); + assert_eq!(updated_post.featured_media, Some(MEDIA_ID_611)); } ); @@ -250,7 +253,7 @@ generate_update_test!( comment_status, PostCommentStatus::Open, |updated_post, updated_post_from_wp_cli| { - assert_eq!(updated_post.comment_status, PostCommentStatus::Open); + assert_eq!(updated_post.comment_status, Some(PostCommentStatus::Open)); assert_eq!( updated_post_from_wp_cli.comment_status, PostCommentStatus::Open.to_string() @@ -263,7 +266,7 @@ generate_update_test!( comment_status, PostCommentStatus::Closed, |updated_post, updated_post_from_wp_cli| { - assert_eq!(updated_post.comment_status, PostCommentStatus::Closed); + assert_eq!(updated_post.comment_status, Some(PostCommentStatus::Closed)); assert_eq!( updated_post_from_wp_cli.comment_status, PostCommentStatus::Closed.to_string() @@ -276,7 +279,7 @@ generate_update_test!( ping_status, PostPingStatus::Open, |updated_post, updated_post_from_wp_cli| { - assert_eq!(updated_post.ping_status, PostPingStatus::Open); + assert_eq!(updated_post.ping_status, Some(PostPingStatus::Open)); assert_eq!( updated_post_from_wp_cli.ping_status, PostPingStatus::Open.to_string() @@ -289,7 +292,7 @@ generate_update_test!( ping_status, PostPingStatus::Closed, |updated_post, updated_post_from_wp_cli| { - assert_eq!(updated_post.ping_status, PostPingStatus::Closed); + assert_eq!(updated_post.ping_status, Some(PostPingStatus::Closed)); assert_eq!( updated_post_from_wp_cli.ping_status, PostPingStatus::Closed.to_string()