summaryrefslogtreecommitdiff
path: root/src/client.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2022-09-08 21:08:48 +0200
committerHampusM <hampus@hampusmat.com>2022-09-08 21:08:48 +0200
commit8a02d3386d4ce0b58de943fcf42bd072af1e0b42 (patch)
tree6656b72ce309ae993029d8cdae26604098d92ba7 /src/client.rs
parentb44463d533ba9b789e3423d670e2ddcc32c1112c (diff)
refactor: move get access token to DeezerClient
Diffstat (limited to 'src/client.rs')
-rw-r--r--src/client.rs113
1 files changed, 99 insertions, 14 deletions
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<AccessTokenResponse> 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<HttpConnector>,
- 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<User, DeezerClientError>
+ pub async fn get_me(
+ &self,
+ access_token: AccessToken,
+ ) -> Result<User, DeezerClientError>
{
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<Vec<UserPlaylist>, 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<Playlist, DeezerClientError>
{
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<Uri, DeezerClientError>
+ /// 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<AccessToken, DeezerClientError>
+ {
+ 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<ErrorResponseBody, _> =
+ 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, DeezerClientError>
{
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)