#![allow(dead_code)]
use crate::prelude::errors::ClientErr;
use crate::prelude::HttpConnection;
use chrono::prelude::*;
use chrono::{DateTime, TimeZone, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use url::Url;
#[derive(Deserialize, Default, Clone, Debug)]
pub struct UserToken {
pub code: String,
pub access_token: String,
pub data_access_expiration_time: String,
pub expires_in: String,
pub long_lived_token: String,
pub state: String,
pub login_error: Option<LoginError>,
url: String,
}
impl UserToken {
pub fn new(access_token: String, long_lived_token: String) -> Self {
UserToken {
code: "".to_string(),
access_token,
data_access_expiration_time: "".to_string(),
expires_in: "".to_string(),
long_lived_token,
state: "".to_string(),
login_error: None,
url: "".to_string(),
}
}
pub fn new_with_url(url: String) -> Self {
UserToken {
code: "".to_string(),
access_token: "".to_string(),
data_access_expiration_time: "".to_string(),
expires_in: "".to_string(),
long_lived_token: "".to_string(),
state: "".to_string(),
login_error: None,
url,
}
}
}
impl UserToken {
pub fn user_access_tokens(self) -> Self {
self
}
pub fn extract_user_tokens(url: String) -> UserToken {
let mut response = UserToken::default();
let updated_url = url.replace("#", "");
let query_params: HashMap<_, _> = Url::parse(&updated_url)
.unwrap()
.query_pairs()
.into_owned()
.collect();
if query_params.is_empty() {
panic!(
"There was no query parameter in uri argument that was passed in. error_message: number of argument found {:?}. The url argument : {} ",
query_params.len(), url
)
}
let mut login_error = LoginError::default();
for params in query_params.clone() {
match params.0.as_str() {
"access_token" => {
response.access_token = params.1;
}
"code" => {
response.code = params.1;
}
"data_access_expiration_time" => {
response.data_access_expiration_time = params.1;
}
"expires_in" => {
response.expires_in = params.1;
}
"long_lived_token" => {
response.long_lived_token = params.1;
}
"state" => {
response.state = params.1;
}
"error" => {
login_error.error = params.1;
}
"error_reason" => {
login_error.error_reason = params.1;
}
"error_description" => {
login_error.error_description = params.1;
}
&_ => {}
}
}
if query_params.contains_key("error") {
response.login_error = Some(login_error)
}
response
}
pub async fn exchange_short_live_for_long_live_token(
self,
short_live_token: String,
app_secret: String,
client_id: String,
redirect_uri: String,
) -> Result<ExchangeToken, ClientErr> {
let url = self.url.replace("NODE/EDGE", "oauth/access_token")
+ "?client_id="
+ &client_id
+ "&client_secret="
+ &app_secret
+ "&fb_exchange_token="
+ &short_live_token
+ "&redirect_uri="
+ &redirect_uri
+ "&grant_type="
+ "fb_exchange_token";
println!("url: {url}");
let access_token = HttpConnection::get::<ExchangeToken>(url, "".to_string()).await?;
Ok(access_token)
}
pub async fn app_access_token_at_server(
self,
app_secret: String,
app_id: String,
) -> Result<String, ClientErr> {
let base_url = "https://graph.facebook.com/oauth/access_token";
let url = format!(
"{}?client_id={}
&client_secret={}
&grant_type=client_credentials",
base_url, app_id, app_secret
);
let access_token = HttpConnection::get::<String>(url, "".to_string()).await?;
Ok(access_token)
}
pub async fn exchange_code_for_access_token_at_server(
self,
code: String,
app_secret: String,
client_id: String,
redirect_uri: String,
) -> Result<ExchangeToken, ClientErr> {
let url = self.url.replace("NODE/EDGE", "oauth/access_token")
+ "?client_id="
+ &client_id
+ "&client_secret="
+ &app_secret
+ "&redirect_uri="
+ &redirect_uri
+ "&code="
+ &code;
let access_token = HttpConnection::get::<ExchangeToken>(url, "".to_string()).await?;
Ok(access_token)
}
pub async fn access_token_information(
valid_access_token: String,
debug_access_token: String,
) -> Result<AccessTokenInformation, ClientErr> {
let url = "https://graph.facebook.com/debug_token?".to_owned()
+ "input_token="
+ &debug_access_token
+ "&access_token="
+ &valid_access_token;
let access_token_response =
HttpConnection::get::<TokenResponseInformation>(url, "".to_string()).await?;
let access_token_expiring_date = access_token_response.data.expires_at.to_owned();
let mut access_token_information = AccessTokenInformation::default();
if access_token_expiring_date != 0 {
let token_expiring_date_utc = Utc.timestamp(access_token_expiring_date, 0);
let token_expiring_date_local: DateTime<Local> =
DateTime::from(token_expiring_date_utc);
access_token_information.expires_at_local_date = token_expiring_date_local.to_rfc2822();
} else {
access_token_information.expires_at_local_date = access_token_expiring_date.to_string();
}
let token_expiring_data_time =
Utc.timestamp(access_token_response.data.data_access_expires_at, 0);
let token_expiring_data_time_local: DateTime<Local> =
DateTime::from(token_expiring_data_time);
access_token_information.data_access_expires_at_local_date =
token_expiring_data_time_local.to_rfc2822();
access_token_information.expires_at = access_token_response.data.expires_at;
access_token_information.data_access_expires_at =
access_token_response.data.data_access_expires_at;
access_token_information.is_valid = access_token_response.data.is_valid;
access_token_information.token_type = access_token_response.data.r#type.clone();
access_token_information.user_id = access_token_response.data.user_id;
access_token_information.app_id = access_token_response.data.app_id;
access_token_information.scopes = access_token_response.data.scopes;
Ok(access_token_information)
}
pub fn set_url(mut self, url: String) -> Self {
self.url = url;
self
}
}
#[derive(Deserialize, Default, Clone, Debug, Serialize)]
pub struct AccessTokenInformation {
pub expires_at: i64,
pub token_type: String,
pub expires_at_local_date: String,
pub is_valid: bool,
pub data_access_expires_at: i64,
pub data_access_expires_at_local_date: String,
pub app_id: String,
pub application: String,
pub scopes: Vec<String>,
pub granular_scopes: Vec<GranularScopes>,
pub user_id: String,
}
#[derive(Deserialize, Clone, Debug)]
struct TokenResponseInformation {
data: TokenResponseData,
}
#[derive(Deserialize, Default, Clone, Debug, Serialize)]
struct TokenResponseData {
expires_at: i64,
r#type: String,
is_valid: bool,
data_access_expires_at: i64,
app_id: String,
application: String,
scopes: Vec<String>,
granular_scopes: Vec<GranularScopes>,
user_id: String,
}
#[derive(Deserialize, Default, Clone, Debug, Serialize)]
pub struct GranularScopes {
scope: String,
}
pub enum TokenLiveType {
LONGLIVE,
SHORTLIVE,
}
#[derive(Deserialize, Clone, Debug)]
pub struct ExchangeToken {
access_token: String,
token_type: String,
expires_in: Option<u32>,
}
impl ExchangeToken {
pub fn access_token(&self) -> &str {
&self.access_token
}
pub fn token_type(&self) -> &str {
&self.token_type
}
pub fn expires_in(&self) -> Option<u32> {
self.expires_in
}
}
#[derive(Deserialize, Default, Clone, Debug)]
pub struct LoginError {
error: String,
error_reason: String,
error_description: String,
}