tiktok_api/endpoints/v2/
user_info.rs1use http_api_client_endpoint::{
2 http::{
3 header::{ACCEPT, AUTHORIZATION, USER_AGENT},
4 Method,
5 },
6 Body, Endpoint, Request, Response,
7};
8use serde::{Deserialize, Serialize};
9use url::Url;
10
11use super::common::{endpoint_parse_response, EndpointError, EndpointRet};
12use crate::objects::v2::{Error, User};
13
14pub const URL: &str = "https://open.tiktokapis.com/v2/user/info/";
16pub const FIELDS_DEFAULT: &str = "open_id,union_id,avatar_url,avatar_url_100,avatar_large_url,display_name,bio_description,profile_deep_link,is_verified,follower_count,following_count,likes_count";
17
18#[derive(Debug, Clone)]
20pub struct UserInfoEndpoint {
21 pub access_token: String,
22 pub fields: String,
23}
24impl UserInfoEndpoint {
25 pub fn new(access_token: impl AsRef<str>) -> Self {
26 Self {
27 access_token: access_token.as_ref().into(),
28 fields: FIELDS_DEFAULT.into(),
29 }
30 }
31
32 pub fn with_fields(mut self, fields: impl AsRef<str>) -> Self {
33 self.fields = fields.as_ref().into();
34 self
35 }
36}
37
38impl Endpoint for UserInfoEndpoint {
39 type RenderRequestError = EndpointError;
40
41 type ParseResponseOutput = EndpointRet<UserInfoResponseBody>;
42 type ParseResponseError = EndpointError;
43
44 fn render_request(&self) -> Result<Request<Body>, Self::RenderRequestError> {
45 let mut url = Url::parse(URL).map_err(EndpointError::MakeRequestUrlFailed)?;
46 url.query_pairs_mut().append_pair("fields", &self.fields);
47
48 let request = Request::builder()
49 .method(Method::GET)
50 .uri(url.as_str())
51 .header(AUTHORIZATION, format!("Bearer {}", &self.access_token))
52 .header(USER_AGENT, "tiktok-api")
53 .header(ACCEPT, "application/json")
54 .body(vec![])
55 .map_err(EndpointError::MakeRequestFailed)?;
56
57 Ok(request)
58 }
59
60 fn parse_response(
61 &self,
62 response: Response<Body>,
63 ) -> Result<Self::ParseResponseOutput, Self::ParseResponseError> {
64 endpoint_parse_response(response)
65 }
66}
67
68#[derive(Serialize, Deserialize, Debug, Clone)]
70pub struct UserInfoResponseBody {
71 pub data: UserInfoResponseBodyData,
72 pub error: Error,
73}
74
75#[derive(Serialize, Deserialize, Debug, Clone)]
76pub struct UserInfoResponseBodyData {
77 pub user: User,
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 use crate::objects::v2::ErrorCode;
85
86 #[test]
87 fn test_render_request() {
88 let req = UserInfoEndpoint::new("TOKEN").render_request().unwrap();
89 assert_eq!(req.method(), Method::GET);
90 assert_eq!(req.uri(), "https://open.tiktokapis.com/v2/user/info/?fields=open_id%2Cunion_id%2Cavatar_url%2Cavatar_url_100%2Cavatar_large_url%2Cdisplay_name%2Cbio_description%2Cprofile_deep_link%2Cis_verified%2Cfollower_count%2Cfollowing_count%2Clikes_count");
91
92 let req = UserInfoEndpoint::new("TOKEN")
93 .with_fields("open_id,union_id,avatar_url")
94 .render_request()
95 .unwrap();
96 assert_eq!(
97 req.uri(),
98 "https://open.tiktokapis.com/v2/user/info/?fields=open_id%2Cunion_id%2Cavatar_url"
99 );
100 }
101
102 #[test]
103 fn test_de_response_body() {
104 match serde_json::from_str::<UserInfoResponseBody>(include_str!(
105 "../../../tests/response_body_files/v2/user_info__full.json"
106 )) {
107 Ok(ok_json) => {
108 assert_eq!(
109 ok_json.data.user.open_id,
110 Some("_000fwZ23Mw4RY9cB4lDQyKCgQg4Ft6SyTuE".into())
111 );
112 assert_eq!(ok_json.error.code, ErrorCode::Ok);
113 }
114 x => panic!("{x:?}"),
115 }
116
117 match serde_json::from_str::<UserInfoResponseBody>(include_str!(
119 "../../../tests/response_body_files/v2/user_info.json"
120 )) {
121 Ok(ok_json) => {
122 assert_eq!(
123 ok_json.data.user.open_id,
124 Some("723f24d7-e717-40f8-a2b6-cb8464cd23b4".into())
125 );
126 assert_eq!(ok_json.error.code, ErrorCode::Ok);
127 }
128 x => panic!("{x:?}"),
129 }
130 }
131}