Skip to content

Commit 20e978c

Browse files
authored
Add WP.com /rest/v1.1/me endpoint (#948)
* Add WP.com `/rest/v1.1/me` endpoint * Add Swift support
1 parent ebba0e0 commit 20e978c

File tree

9 files changed

+644
-0
lines changed

9 files changed

+644
-0
lines changed

native/swift/Sources/wordpress-api/WPComApiClient.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ public class WPComApiClient {
1010
self.internalClient = UniffiWpComApiClient(delegate: delegate)
1111
}
1212

13+
// swiftlint:disable:next identifier_name
14+
public var me: MeRequestExecutor {
15+
internalClient.me()
16+
}
17+
1318
public var oauth2: Oauth2RequestExecutor {
1419
internalClient.oauth2()
1520
}

wp_api/src/wp_com/client.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use super::endpoint::{
33
jetpack_connection_endpoint::{
44
JetpackConnectionRequestBuilder, JetpackConnectionRequestExecutor,
55
},
6+
me_endpoint::{MeRequestBuilder, MeRequestExecutor},
67
oauth2::{Oauth2RequestBuilder, Oauth2RequestExecutor},
78
subscribers_endpoint::{SubscribersRequestBuilder, SubscribersRequestExecutor},
89
support_bots_endpoint::{SupportBotsRequestBuilder, SupportBotsRequestExecutor},
@@ -37,6 +38,7 @@ impl UniffiWpComApiRequestBuilder {
3738
pub struct WpComApiRequestBuilder {
3839
followers: Arc<FollowersRequestBuilder>,
3940
jetpack_connection: Arc<JetpackConnectionRequestBuilder>,
41+
me: Arc<MeRequestBuilder>,
4042
oauth2: Arc<Oauth2RequestBuilder>,
4143
subscribers: Arc<SubscribersRequestBuilder>,
4244
support_bots: Arc<SupportBotsRequestBuilder>,
@@ -53,6 +55,7 @@ impl WpComApiRequestBuilder {
5355
auth_provider;
5456
followers,
5557
jetpack_connection,
58+
me,
5659
oauth2,
5760
subscribers,
5861
support_bots,
@@ -80,6 +83,7 @@ impl UniffiWpComApiClient {
8083
pub struct WpComApiClient {
8184
followers: Arc<FollowersRequestExecutor>,
8285
jetpack_connection: Arc<JetpackConnectionRequestExecutor>,
86+
me: Arc<MeRequestExecutor>,
8387
oauth2: Arc<Oauth2RequestExecutor>,
8488
subscribers: Arc<SubscribersRequestExecutor>,
8589
support_bots: Arc<SupportBotsRequestExecutor>,
@@ -97,6 +101,7 @@ impl WpComApiClient {
97101
delegate;
98102
followers,
99103
jetpack_connection,
104+
me,
100105
oauth2,
101106
subscribers,
102107
support_bots,
@@ -107,6 +112,7 @@ impl WpComApiClient {
107112
}
108113
api_client_generate_endpoint_impl!(WpComApi, followers);
109114
api_client_generate_endpoint_impl!(WpComApi, jetpack_connection);
115+
api_client_generate_endpoint_impl!(WpComApi, me);
110116
api_client_generate_endpoint_impl!(WpComApi, oauth2);
111117
api_client_generate_endpoint_impl!(WpComApi, subscribers);
112118
api_client_generate_endpoint_impl!(WpComApi, support_bots);

wp_api/src/wp_com/endpoint.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use strum::IntoEnumIterator;
99
pub mod extensions;
1010
pub mod followers_endpoint;
1111
pub mod jetpack_connection_endpoint;
12+
pub mod me_endpoint;
1213
pub mod oauth2;
1314
pub mod subscribers_endpoint;
1415
pub mod support_bots_endpoint;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use crate::wp_com::me::WPComUserInfo;
2+
use crate::{
3+
request::endpoint::{AsNamespace, DerivedRequest},
4+
wp_com::WpComNamespace,
5+
};
6+
use wp_derive_request_builder::WpDerivedRequest;
7+
8+
#[derive(WpDerivedRequest)]
9+
enum MeRequest {
10+
#[get(url = "/me", output = WPComUserInfo)]
11+
Get,
12+
}
13+
14+
impl DerivedRequest for MeRequest {
15+
fn namespace() -> impl AsNamespace {
16+
WpComNamespace::RestV1_1
17+
}
18+
}

wp_api/src/wp_com/me.rs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
use crate::date::WpGmtDateTime;
2+
use serde::{Deserialize, Serialize};
3+
use std::collections::HashMap;
4+
use wp_serde_helper::{
5+
deserialize_empty_string_as_none, deserialize_null_as_empty_vec,
6+
deserialize_string_vec_or_string, deserialize_u64_or_none,
7+
deserialize_u64_or_none_with_negative_as_none, deserialize_u64_or_none_with_zero_as_none,
8+
};
9+
10+
#[derive(Debug, Serialize, Deserialize, uniffi::Record)]
11+
pub struct WPComUserInfo {
12+
/// The user's WP.com ID.
13+
#[serde(rename = "ID")]
14+
pub id: u64,
15+
16+
/// The user's display name as set in the `Public display name` field
17+
pub display_name: String,
18+
19+
/// The user's username as set at account creation. This cannot be changed.
20+
pub username: String,
21+
22+
/// The user's email address.
23+
pub email: String,
24+
25+
/// The user's primary blog ID – this is the one that was created when they made their account.
26+
#[serde(rename = "primary_blog")]
27+
#[serde(deserialize_with = "deserialize_u64_or_none")]
28+
pub primary_blog_id: Option<u64>,
29+
30+
/// The user's primary blog URL – this is the one that was created when they made their account.
31+
pub primary_blog_url: Option<String>,
32+
33+
/// Whether the user's primary blog is a Jetpack blog.
34+
pub primary_blog_is_jetpack: bool,
35+
36+
/// Whether the user has Jetpack partner access.
37+
pub has_jetpack_partner_access: bool,
38+
39+
/// The partner types of the partner accounts this user has access to.
40+
#[serde(default)]
41+
pub jetpack_partner_types: Vec<String>,
42+
43+
/// The user's preferred language.
44+
pub language: String,
45+
46+
/// The variant of the user's preferred language.
47+
#[serde(deserialize_with = "deserialize_empty_string_as_none")]
48+
pub locale_variant: Option<String>,
49+
50+
/// If the current access token is scoped to a specific Site ID, this field will be set to that Site ID. Otherwise, it will be null.
51+
#[serde(deserialize_with = "deserialize_u64_or_none_with_zero_as_none")]
52+
pub token_site_id: Option<u64>,
53+
54+
/// The scopes of the current access token – see https://developer.wordpress.com/docs/api/oauth2/ for a list of possible values.
55+
#[serde(rename = "token_scope")]
56+
#[serde(deserialize_with = "deserialize_string_vec_or_string")]
57+
pub token_scopes: Vec<String>,
58+
59+
/// If the current access token is scoped to a specific Client ID, this field will be set to that Client ID. Otherwise, it will be null.
60+
#[serde(deserialize_with = "deserialize_u64_or_none_with_negative_as_none")]
61+
pub token_client_id: Option<u64>,
62+
63+
/// The user's avatar URL as set on WordPress.com or using Gravatar.
64+
pub avatar_url: Option<String>,
65+
66+
/// The user's Gravatar profile URL.
67+
pub profile_url: Option<String>,
68+
69+
/// Whether the user's email address has been verified via WordPress.com Connect.
70+
pub verified: bool,
71+
72+
/// Whether the user's email address has been verified – their ability to perform many actions requires this to be true.
73+
pub email_verified: bool,
74+
75+
/// The date of the user's account creation.
76+
#[serde(rename = "date")]
77+
pub creation_date: WpGmtDateTime,
78+
79+
/// The number of sites the user has access to.
80+
pub site_count: u64,
81+
82+
/// The number of sites the user has access to that are Jetpack sites.
83+
pub jetpack_site_count: u64,
84+
85+
/// The number of sites the user has access to that are Atomic sites.
86+
pub atomic_site_count: u64,
87+
88+
/// The number of sites the user has access to that are visible.
89+
pub visible_site_count: u64,
90+
91+
/// The number of visible sites the user has access to that are Jetpack sites.
92+
pub jetpack_visible_site_count: u64,
93+
94+
/// The number of visible sites the user has access to that are Atomic sites.
95+
pub atomic_visible_site_count: u64,
96+
97+
/// Whether the user has unseen notifications.
98+
pub has_unseen_notes: bool,
99+
100+
/// The type of the user's newest notification.
101+
pub newest_note_type: Option<String>,
102+
103+
/// If this is a phone account then the user doesn't have a verified email address
104+
pub phone_account: bool,
105+
106+
/// Is the user somewhere where Google Workspace can be purchased?
107+
pub is_valid_google_apps_country: bool,
108+
109+
/// Country code for the user's IP address.
110+
pub user_ip_country_code: Option<String>,
111+
112+
/// Active social login connections.
113+
#[serde(deserialize_with = "deserialize_null_as_empty_vec")]
114+
pub social_login_connections: Vec<WpComSocialLoginConnection>,
115+
116+
/// The name of the social service this account is linked to.
117+
pub social_signup_service: Option<String>,
118+
119+
/// User's assigned A/B test variations, where the key is the test name and the value is the variation
120+
pub abtests: HashMap<String, String>,
121+
}
122+
123+
#[derive(Debug, Serialize, Deserialize, uniffi::Record)]
124+
pub struct WpComSocialLoginConnection {
125+
pub service: String,
126+
pub service_user_email: String,
127+
pub service_user_id: String,
128+
}
129+
130+
#[cfg(test)]
131+
mod tests {
132+
use rstest::rstest;
133+
use std::io::Read;
134+
135+
use super::*;
136+
137+
#[rstest]
138+
#[case("v1.1-me-01.json", 742098)]
139+
#[case("v1.1-me-02.json", 158350866)]
140+
// IDs for this test aren't anonymized so that they can be checked later if needed.
141+
fn test_wpcom_user_info_deserialization(
142+
#[case] json_file_path: &str,
143+
#[case] expected_id: u64,
144+
) {
145+
let json = test_json(json_file_path).expect("Failed to read JSON file");
146+
let user_info: WPComUserInfo =
147+
serde_json::from_slice(json.as_slice()).expect("Failed to deserialize user info");
148+
assert_eq!(user_info.id, expected_id);
149+
}
150+
151+
fn test_json(input: &str) -> Result<Vec<u8>, std::io::Error> {
152+
let mut file_path = std::path::PathBuf::from(env!("CARGO_WORKSPACE_DIR"));
153+
file_path.push("wp_api");
154+
file_path.push("tests");
155+
file_path.push("wpcom");
156+
file_path.push("me");
157+
file_path.push(input);
158+
159+
let mut f = std::fs::File::open(file_path)?;
160+
let mut buffer = Vec::new();
161+
162+
// read the whole file
163+
f.read_to_end(&mut buffer)?;
164+
165+
Ok(buffer)
166+
}
167+
}

wp_api/src/wp_com/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod client;
77
pub mod endpoint;
88
pub mod followers;
99
pub mod jetpack_connection;
10+
pub mod me;
1011
pub mod oauth2;
1112
pub mod subscribers;
1213
pub mod support_bots;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"ID": 742098,
3+
"display_name": "Test User",
4+
"username": "testuser",
5+
"email": "[email protected]",
6+
"primary_blog": 654321,
7+
"primary_blog_url": "http://testuser.example.com",
8+
"primary_blog_is_jetpack": false,
9+
"has_jetpack_partner_access": false,
10+
"language": "en",
11+
"locale_variant": "",
12+
"token_site_id": false,
13+
"token_scope": "global",
14+
"token_client_id": -1,
15+
"avatar_URL": "https://example.com/avatar.png",
16+
"profile_URL": "http://gravatar.com/testuser",
17+
"verified": true,
18+
"email_verified": true,
19+
"date": "2000-01-01T00:00:00+00:00",
20+
"site_count": 10,
21+
"jetpack_site_count": 1,
22+
"atomic_site_count": 1,
23+
"visible_site_count": 5,
24+
"jetpack_visible_site_count": 1,
25+
"atomic_visible_site_count": 1,
26+
"has_unseen_notes": false,
27+
"newest_note_type": "comment",
28+
"phone_account": false,
29+
"meta": {
30+
"links": {
31+
"self": "https://public-api.wordpress.com/rest/v1.1/me",
32+
"help": "https://public-api.wordpress.com/rest/v1.1/me/help",
33+
"site": "https://public-api.wordpress.com/rest/v1.1/sites/654321",
34+
"flags": "https://public-api.wordpress.com/rest/v1.1/me/flags"
35+
},
36+
"marketing_price_group": false,
37+
"plans_reorder_abtest_variation": "control"
38+
},
39+
"is_valid_google_apps_country": true,
40+
"user_ip_country_code": "US",
41+
"logout_URL": "https://wordpress.com/wp-login.php?action=logout&_wpnonce=asdf1234&redirect_to=https%3A%2F%2Fwordpress.com%2F",
42+
"social_login_connections": null,
43+
"social_signup_service": null,
44+
"abtests": {},
45+
"lasagna_jwt": "test.jwt.token",
46+
"i18n_empathy_mode": false,
47+
"use_fallback_for_incomplete_languages": false,
48+
"is_google_domain_owner": false,
49+
"had_hosting_trial": false,
50+
"is_subscription_only": false
51+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"ID": 158350866,
3+
"display_name": "Test User",
4+
"username": "testuser",
5+
"email": "[email protected]",
6+
"primary_blog": 200000001,
7+
"primary_blog_url": "https:\/\/testuser.example.blog",
8+
"primary_blog_is_jetpack": false,
9+
"has_jetpack_partner_access": false,
10+
"language": "en",
11+
"locale_variant": "",
12+
"token_site_id": 0,
13+
"token_scope": "global",
14+
"token_client_id": 11,
15+
"avatar_URL": "https:\/\/0.gravatar.com\/avatar\/0000000000000000000000000000000000000000000000000000000000000000?s=96&d=identicon",
16+
"profile_URL": "http:\/\/gravatar.com\/testuser",
17+
"verified": true,
18+
"email_verified": true,
19+
"date": "2019-05-31T23:38:40+00:00",
20+
"site_count": 6,
21+
"jetpack_site_count": 1,
22+
"atomic_site_count": 1,
23+
"visible_site_count": 6,
24+
"jetpack_visible_site_count": 1,
25+
"atomic_visible_site_count": 1,
26+
"has_unseen_notes": false,
27+
"newest_note_type": "",
28+
"phone_account": false,
29+
"meta": {
30+
"links": {
31+
"self": "https:\/\/public-api.wordpress.com\/rest\/v1.1\/me",
32+
"help": "https:\/\/public-api.wordpress.com\/rest\/v1.1\/me\/help",
33+
"site": "https:\/\/public-api.wordpress.com\/rest\/v1.1\/sites\/100000002",
34+
"flags": "https:\/\/public-api.wordpress.com\/rest\/v1.1\/me\/flags"
35+
},
36+
"marketing_price_group": false,
37+
"plans_reorder_abtest_variation": "control"
38+
},
39+
"is_valid_google_apps_country": true,
40+
"user_ip_country_code": "US",
41+
"logout_URL": "https:\/\/wordpress.com\/wp-login.php?action=logout&_wpnonce=0000000000&redirect_to=https%3A%2F%2Fwordpress.com%2F",
42+
"social_login_connections": [
43+
{
44+
"service": "google",
45+
"service_user_email": "[email protected]",
46+
"service_user_id": "999999999999999999999"
47+
}
48+
],
49+
"social_signup_service": "google",
50+
"abtests": {},
51+
"i18n_empathy_mode": false,
52+
"use_fallback_for_incomplete_languages": false,
53+
"is_google_domain_owner": false,
54+
"had_hosting_trial": false,
55+
"is_subscription_only": false
56+
}

0 commit comments

Comments
 (0)