Skip to content

Commit 8e174b7

Browse files
Add an 'extras' property to comments (#807)
* Add an 'extras' property to comments * Parse additional data in WP.com comments API * Use the new attribute * Fix WpContextualExcludeFromFields filtering in test helpers Exclude fields marked with WpContextualExcludeFromFields from integration test helper generation to prevent referencing non-existent enum variants. * Rename the parse_extension function * Move wpcom comments tests * Breakdown into three test functions for each context * Format code --------- Co-authored-by: Oguz Kocer <[email protected]>
1 parent f6a010e commit 8e174b7

File tree

11 files changed

+202
-5
lines changed

11 files changed

+202
-5
lines changed

wp_api/src/comments.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
UserAvatarSize, UserId, WpApiParamOrder, WpResponseString,
2+
AnyJson, UserAvatarSize, UserId, WpApiParamOrder, WpResponseString,
33
date::WpGmtDateTime,
44
impl_as_query_value_for_new_type, impl_as_query_value_from_to_string,
55
posts::PostId,
@@ -8,7 +8,7 @@ use crate::{
88
},
99
};
1010
use serde::{Deserialize, Serialize};
11-
use std::{collections::HashMap, num::ParseIntError, str::FromStr};
11+
use std::{collections::HashMap, num::ParseIntError, str::FromStr, sync::Arc};
1212
use strum_macros::IntoStaticStr;
1313
use wp_contextual::WpContextual;
1414

@@ -528,6 +528,10 @@ pub struct SparseComment {
528528
pub comment_type: Option<CommentType>,
529529
#[WpContext(edit, embed, view)]
530530
pub author_avatar_urls: Option<HashMap<UserAvatarSize, WpResponseString>>,
531+
#[serde(flatten)]
532+
#[WpContext(edit, embed, view)]
533+
#[WpContextualExcludeFromFields]
534+
pub additional_fields: Option<Arc<AnyJson>>,
531535
// meta field is omitted for now: https://github.com/Automattic/wordpress-rs/issues/422
532536
}
533537

wp_api/src/lib.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use plugins::*;
22
use serde::{Deserialize, Serialize};
3+
use serde_json::Value;
34
use std::collections::HashMap;
45
use users::*;
56
use wp_localization::{MessageBundle, WpMessages, WpSupportsLocalization};
@@ -118,6 +119,14 @@ pub enum JsonValue {
118119
Object(HashMap<String, JsonValue>),
119120
}
120121

122+
/// Similar to `JsonValue`, but exported as a Uniffi object.
123+
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, uniffi::Object)]
124+
#[uniffi::export(Eq, Hash)]
125+
pub struct AnyJson {
126+
#[serde(flatten)]
127+
pub raw: Value,
128+
}
129+
121130
uniffi::custom_newtype!(WpResponseString, Option<String>);
122131
#[derive(Debug, Serialize, Deserialize)]
123132
#[serde(try_from = "BoolOrString")]
@@ -235,4 +244,30 @@ mod tests {
235244
fn test_orderby_string_conversion(#[case] orderby: WpApiParamOrder) {
236245
assert_eq!(orderby, orderby.to_string().parse().unwrap());
237246
}
247+
248+
#[derive(Deserialize, Debug)]
249+
struct Person {
250+
name: String,
251+
#[serde(flatten)]
252+
other_fields: AnyJson,
253+
}
254+
255+
#[test]
256+
fn test_parse_any_json() {
257+
let json = r#"{"name": "Alice", "age": 30, "city": "Wonderland"}"#;
258+
let person: Person = serde_json::from_str(json).unwrap();
259+
assert_eq!(person.name, "Alice");
260+
assert_eq!(
261+
person.other_fields.raw,
262+
serde_json::json!({"age": 30, "city": "Wonderland"})
263+
);
264+
}
265+
266+
#[test]
267+
fn test_parse_empty_any_json() {
268+
let json = r#"{"name": "Alice"}"#;
269+
let person: Person = serde_json::from_str(json).unwrap();
270+
assert_eq!(person.name, "Alice");
271+
assert_eq!(person.other_fields.raw, serde_json::json!({}));
272+
}
238273
}

wp_api/src/uniffi_serde.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use wp_localization::{MessageBundle, WpMessages, WpSupportsLocalization};
22
use wp_localization_macro::WpDeriveLocalizable;
33

44
#[derive(Debug, thiserror::Error, uniffi::Error, WpDeriveLocalizable)]
5-
pub(crate) enum UniffiSerializationError {
5+
pub enum UniffiSerializationError {
66
Serde { reason: String },
77
}
88

wp_api/src/wp_com/endpoint.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{
66
use std::sync::Arc;
77
use strum::IntoEnumIterator;
88

9+
pub mod extensions;
910
pub mod followers_endpoint;
1011
pub mod jetpack_connection_endpoint;
1112
pub mod oauth2;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use serde::Deserialize;
2+
3+
use crate::{AnyJson, uniffi_serde::UniffiSerializationError};
4+
5+
#[derive(Debug, Deserialize, uniffi::Record)]
6+
pub struct WpComCommentExtension {
7+
#[serde(rename = "extended_post")]
8+
pub post: Option<WpComCommentExtensionPostInfo>,
9+
#[serde(rename = "extended_i_replied")]
10+
pub i_replied: bool,
11+
#[serde(rename = "extended_like_count")]
12+
pub like_count: u32,
13+
#[serde(rename = "extended_i_like")]
14+
pub i_like: bool,
15+
}
16+
17+
#[derive(Debug, Deserialize, uniffi::Record)]
18+
pub struct WpComCommentExtensionPostInfo {
19+
pub id: u64,
20+
pub title: String,
21+
#[serde(rename = "type")]
22+
pub kind: String,
23+
pub link: String,
24+
}
25+
26+
#[uniffi::export(with_foreign)]
27+
pub trait WpComCommentExtensionProvider: Send + Sync {
28+
fn parse_wpcom_comments_extension(
29+
&self,
30+
) -> Result<WpComCommentExtension, UniffiSerializationError>;
31+
}
32+
33+
#[uniffi::export]
34+
impl WpComCommentExtensionProvider for AnyJson {
35+
fn parse_wpcom_comments_extension(
36+
&self,
37+
) -> Result<WpComCommentExtension, UniffiSerializationError> {
38+
serde_json::to_string(&self.raw)
39+
.and_then(|json| serde_json::from_str(&json))
40+
.map_err(Into::into)
41+
}
42+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod comments;

wp_api_integration_tests/src/lib.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::prelude::*;
2-
use wp_api::wp_com::client::WpComApiClient;
2+
use wp_api::wp_com::{WpComBaseUrl, client::WpComApiClient, endpoint::WpComDotOrgApiUrlResolver};
33

44
pub mod mock;
55
pub mod prelude;
@@ -42,6 +42,7 @@ pub struct WpComTestCredentials {
4242
pub site_id: u64,
4343
pub wp_com_subscriber_user_id: i64,
4444
pub email_subscriber_subscription_id: u64,
45+
pub comment_id: i64,
4546
}
4647

4748
pub mod backend;
@@ -152,6 +153,25 @@ pub fn wp_com_client() -> WpComApiClient {
152153
})
153154
}
154155

156+
pub fn api_client_backed_by_wp_com(site_id: String) -> WpApiClient {
157+
WpApiClient::new(
158+
Arc::new(WpComDotOrgApiUrlResolver::new(
159+
site_id,
160+
WpComBaseUrl::Production,
161+
)),
162+
WpApiClientDelegate {
163+
auth_provider: Arc::new(WpAuthenticationProvider::static_with_auth(
164+
WpAuthentication::Bearer {
165+
token: WpComTestCredentials::instance().bearer_token.to_string(),
166+
},
167+
)),
168+
request_executor: Arc::new(ReqwestRequestExecutor::default()),
169+
middleware_pipeline: Arc::new(WpApiMiddlewarePipeline::default()),
170+
app_notifier: Arc::new(EmptyAppNotifier),
171+
},
172+
)
173+
}
174+
155175
pub fn test_site_url() -> ParsedUrl {
156176
let mut url: Url = TestCredentials::instance()
157177
.site_url

wp_api_integration_tests/tests/test_comments_immut.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use serde_json::Value;
12
use wp_api::{
23
comments::{
34
CommentId, CommentListParams, CommentRetrieveParams, CommentStatus, CommentType,
@@ -207,6 +208,23 @@ async fn list_comments_with_edit_context_parse_author_avatar_urls(
207208
});
208209
}
209210

211+
#[tokio::test]
212+
#[parallel]
213+
async fn parse_extras() {
214+
let comment = api_client()
215+
.comments()
216+
.retrieve_with_edit_context(&FIRST_COMMENT_ID, &CommentRetrieveParams::default())
217+
.await
218+
.assert_response()
219+
.data;
220+
match comment.additional_fields.raw {
221+
Value::Object(ref map) => {
222+
assert!(map.contains_key("_links"));
223+
}
224+
_ => panic!("Expected extras to be an object"),
225+
}
226+
}
227+
210228
#[template]
211229
#[rstest]
212230
#[case::default(CommentListParams::default())]
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use wp_api::{
2+
comments::{CommentId, CommentRetrieveParams},
3+
wp_com::endpoint::extensions::comments::WpComCommentExtensionProvider,
4+
};
5+
6+
use wp_api_integration_tests::{WpComTestCredentials, api_client_backed_by_wp_com, prelude::*};
7+
8+
#[tokio::test]
9+
#[parallel]
10+
#[ignore]
11+
async fn parse_extension_view_context() {
12+
let site_id = WpComTestCredentials::instance().site_id.to_string();
13+
let comment_id = CommentId(WpComTestCredentials::instance().comment_id);
14+
let client = api_client_backed_by_wp_com(site_id);
15+
16+
let comment = client
17+
.comments()
18+
.retrieve_with_view_context(&comment_id, &CommentRetrieveParams::default())
19+
.await
20+
.assert_response()
21+
.data;
22+
assert!(
23+
comment
24+
.additional_fields
25+
.parse_wpcom_comments_extension()
26+
.is_ok()
27+
);
28+
}
29+
30+
#[tokio::test]
31+
#[parallel]
32+
#[ignore]
33+
async fn parse_extension_edit_context() {
34+
let site_id = WpComTestCredentials::instance().site_id.to_string();
35+
let comment_id = CommentId(WpComTestCredentials::instance().comment_id);
36+
let client = api_client_backed_by_wp_com(site_id);
37+
38+
let comment = client
39+
.comments()
40+
.retrieve_with_edit_context(&comment_id, &CommentRetrieveParams::default())
41+
.await
42+
.assert_response()
43+
.data;
44+
assert!(
45+
comment
46+
.additional_fields
47+
.parse_wpcom_comments_extension()
48+
.is_ok()
49+
);
50+
}
51+
52+
#[tokio::test]
53+
#[parallel]
54+
#[ignore]
55+
async fn parse_extension_embed_context() {
56+
let site_id = WpComTestCredentials::instance().site_id.to_string();
57+
let comment_id = CommentId(WpComTestCredentials::instance().comment_id);
58+
let client = api_client_backed_by_wp_com(site_id);
59+
60+
let comment = client
61+
.comments()
62+
.retrieve_with_embed_context(&comment_id, &CommentRetrieveParams::default())
63+
.await
64+
.assert_response()
65+
.data;
66+
assert!(
67+
comment
68+
.additional_fields
69+
.parse_wpcom_comments_extension()
70+
.is_ok()
71+
);
72+
}

wp_com_test_credentials.json-example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"bearer_token": "replace_with_your_oauth2_token",
33
"site_id": 0,
44
"wp_com_subscriber_user_id": 0,
5-
"email_subscriber_subscription_id": 0
5+
"email_subscriber_subscription_id": 0,
6+
"comment_id": 0
67
}

0 commit comments

Comments
 (0)