From 8a02d3386d4ce0b58de943fcf42bd072af1e0b42 Mon Sep 17 00:00:00 2001 From: HampusM Date: Thu, 8 Sep 2022 21:08:48 +0200 Subject: refactor: move get access token to DeezerClient --- src/client.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 99 insertions(+), 14 deletions(-) (limited to 'src/client.rs') diff --git a/src/client.rs b/src/client.rs index 0699545..179377f 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,11 +1,12 @@ //! Deezer client. use std::fmt::Debug; +use std::time::Duration; use hyper::client::{Client, HttpConnector}; use hyper::Uri; use serde::Deserialize; -use crate::auth::AccessToken; +use crate::auth::{AccessToken, AuthCode}; use crate::errors::client::DeezerClientError; use crate::playlist::Playlist; use crate::user::{User, UserPlaylist, UserPlaylists}; @@ -31,24 +32,45 @@ struct ErrorResponseBody error: DeezerError, } +#[derive(Debug, Deserialize)] +struct AccessTokenResponse +{ + pub access_token: String, + pub expires: u64, +} + +impl From for AccessToken +{ + fn from(response: AccessTokenResponse) -> Self + { + Self { + access_token: response.access_token, + expires: Duration::from_secs(response.expires), + } + } +} + /// Deezer client. +#[derive(Default)] pub struct DeezerClient { client: Client, - api_url: &'static str, - access_token: AccessToken, + api_uri_authority: &'static str, + access_token_uri_authority: &'static str, + access_token_uri_path: &'static str, } impl DeezerClient { /// Creates a new Deezer client. #[must_use] - pub fn new(access_token: AccessToken) -> Self + pub fn new() -> Self { Self { client: Client::new(), - api_url: "api.deezer.com", - access_token, + api_uri_authority: "api.deezer.com", + access_token_uri_authority: "connect.deezer.com", + access_token_uri_path: "/oauth/access_token.php", } } @@ -56,11 +78,17 @@ impl DeezerClient /// /// # Errors /// Will return Err if either sending the request or parsing the response fails. - pub async fn get_me(&self) -> Result + pub async fn get_me( + &self, + access_token: AccessToken, + ) -> Result { let response = self .client - .get(self.build_endpoint_uri(&"user/me".to_string())?) + .get(self.build_endpoint_uri( + &"user/me".to_string(), + &[("access_token", access_token.access_token)], + )?) .await?; let body_buf = &*hyper::body::to_bytes(response).await?; @@ -82,11 +110,15 @@ impl DeezerClient pub async fn get_user_playlists( &self, user_id: u64, + access_token: AccessToken, ) -> Result, DeezerClientError> { let response = self .client - .get(self.build_endpoint_uri(&format!("user/{}/playlists", user_id))?) + .get(self.build_endpoint_uri( + &format!("user/{}/playlists", user_id), + &[("access_token", access_token.access_token)], + )?) .await?; let body_buf = &*hyper::body::to_bytes(response).await?; @@ -111,11 +143,15 @@ impl DeezerClient pub async fn get_playlist( &self, playlist_id: u64, + access_token: AccessToken, ) -> Result { let response = self .client - .get(self.build_endpoint_uri(&format!("playlist/{}", playlist_id))?) + .get(self.build_endpoint_uri( + &format!("playlist/{}", playlist_id), + &[("access_token", access_token.access_token)], + )?) .await?; let body_buf = &*hyper::body::to_bytes(response).await?; @@ -130,14 +166,63 @@ impl DeezerClient serde_json::from_slice(body_buf).map_err(DeezerClientError::ParseResponseFailed) } - fn build_endpoint_uri(&self, endpoint: &String) -> Result + /// Returns a access token. + /// + /// # Errors + /// Will return Err if either sending the request or parsing the response fails. + pub async fn get_access_token( + &self, + app_id: u32, + secret_key: String, + auth_code: AuthCode, + ) -> Result + { + let uri = Uri::builder() + .scheme("http") + .authority(self.access_token_uri_authority) + .path_and_query(format!( + "{}?app_id={}&secret={}&code={}&output=json", + self.access_token_uri_path, app_id, secret_key, auth_code + )) + .build() + .map_err(|_| DeezerClientError::BuildAPIEndpointURIFailed)?; + + let response = self.client.get(uri).await?; + + let body_buf = &*hyper::body::to_bytes(response).await?; + + let err_body_result: Result = + serde_json::from_slice(body_buf); + + if let Ok(err_body) = err_body_result { + return Err(DeezerClientError::ReceivedErrorResponse(err_body.error)); + } + + let access_token_response: AccessTokenResponse = serde_json::from_slice(body_buf) + .map_err(DeezerClientError::ParseResponseFailed)?; + + Ok(access_token_response.into()) + } + + fn build_endpoint_uri( + &self, + endpoint: &String, + query_params: &[(&'static str, String)], + ) -> Result { Uri::builder() .scheme("http") - .authority(self.api_url) + .authority(self.api_uri_authority) .path_and_query(format!( - "/{}?access_token={}", - endpoint, self.access_token.access_token + "/{}?{}", + endpoint, + query_params + .iter() + .map(|(key, value)| format!("{}={}", key, value)) + .fold(String::new(), |acc, query_param| format!( + "{}&{}", + acc, query_param + )) )) .build() .map_err(|_| DeezerClientError::BuildAPIEndpointURIFailed) -- cgit v1.2.3-18-g5258