omni_dev/drive/
about_api.rs1use anyhow::Result;
8use serde::Deserialize;
9use url::Url;
10
11use crate::drive::client::DriveClient;
12
13#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
16pub struct AboutUser {
17 #[serde(default, rename = "emailAddress")]
19 pub email_address: Option<String>,
20 #[serde(default, rename = "displayName")]
22 pub display_name: Option<String>,
23}
24
25#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
27pub struct About {
28 pub user: AboutUser,
30}
31
32#[derive(Debug)]
34pub struct AboutApi<'a> {
35 client: &'a DriveClient,
36}
37
38impl<'a> AboutApi<'a> {
39 #[must_use]
41 pub fn new(client: &'a DriveClient) -> Self {
42 Self { client }
43 }
44
45 pub async fn get(&self) -> Result<About> {
47 let url = build_about_url(self.client.base_url())?;
48 self.client
49 .get_parsed(url.as_str(), "Failed to parse about.get response")
50 .await
51 }
52}
53
54fn build_about_url(base_url: &str) -> Result<Url> {
55 let mut url = DriveClient::api_url(base_url, "/drive/v3/about")?;
56 url.query_pairs_mut()
57 .append_pair("fields", "user(emailAddress,displayName)");
58 Ok(url)
59}
60
61#[cfg(test)]
62#[allow(clippy::unwrap_used, clippy::expect_used)]
63mod tests {
64 use super::*;
65 use crate::drive::auth::{DriveCredentials, DriveScope};
66 use crate::utils::secret::Secret;
67
68 fn test_credentials() -> DriveCredentials {
69 DriveCredentials {
70 client_id: "client-1".to_string(),
71 client_secret: Secret::new("secret-1"),
72 refresh_token: Secret::new("refresh-1"),
73 scope: DriveScope::ReadOnly,
74 }
75 }
76
77 async fn client_with_bootstrapped_token(server: &wiremock::MockServer) -> DriveClient {
78 wiremock::Mock::given(wiremock::matchers::method("POST"))
79 .and(wiremock::matchers::path("/token"))
80 .respond_with(
81 wiremock::ResponseTemplate::new(200).set_body_json(serde_json::json!({
82 "access_token": "test-token",
83 "expires_in": 3600,
84 })),
85 )
86 .mount(server)
87 .await;
88
89 let mut client = DriveClient::new(&server.uri(), &test_credentials()).unwrap();
90 crate::drive::client::test_support::replace_session(
91 &mut client,
92 &test_credentials(),
93 &format!("{}/token", server.uri()),
94 );
95 client
96 }
97
98 #[test]
99 fn build_about_url_includes_fields_query_param() {
100 let url = build_about_url("https://www.googleapis.com").unwrap();
101 assert!(url.as_str().contains("/drive/v3/about"));
102 assert!(url.as_str().contains("fields="));
103 }
104
105 #[tokio::test]
106 async fn get_parses_user_fields() {
107 let server = wiremock::MockServer::start().await;
108 let client = client_with_bootstrapped_token(&server).await;
109 wiremock::Mock::given(wiremock::matchers::method("GET"))
110 .and(wiremock::matchers::path("/drive/v3/about"))
111 .respond_with(
112 wiremock::ResponseTemplate::new(200).set_body_json(serde_json::json!({
113 "user": {
114 "emailAddress": "user@example.com",
115 "displayName": "User Name",
116 }
117 })),
118 )
119 .expect(1)
120 .mount(&server)
121 .await;
122
123 let about = AboutApi::new(&client).get().await.unwrap();
124 assert_eq!(
125 about.user.email_address.as_deref(),
126 Some("user@example.com")
127 );
128 assert_eq!(about.user.display_name.as_deref(), Some("User Name"));
129 }
130
131 #[tokio::test]
132 async fn get_propagates_api_errors() {
133 let server = wiremock::MockServer::start().await;
134 let client = client_with_bootstrapped_token(&server).await;
135 wiremock::Mock::given(wiremock::matchers::method("GET"))
136 .and(wiremock::matchers::path("/drive/v3/about"))
137 .respond_with(wiremock::ResponseTemplate::new(403).set_body_string("Forbidden"))
138 .mount(&server)
139 .await;
140
141 let err = AboutApi::new(&client).get().await.unwrap_err();
142 assert!(err.to_string().contains("403"));
143 }
144
145 #[tokio::test]
146 async fn get_rejects_invalid_base_url() {
147 let client = DriveClient::new("not a url", &test_credentials()).unwrap();
148 let err = AboutApi::new(&client).get().await.unwrap_err();
149 assert!(err.to_string().contains("Invalid Drive base URL"));
150 }
151}