Get token (#8)

Reviewed-on: #8
This commit was merged in pull request #8.
This commit is contained in:
2026-06-24 14:50:15 -04:00
parent 702fead8c8
commit b5bb605361
7 changed files with 193 additions and 28 deletions
Generated
+1
View File
@@ -136,6 +136,7 @@ dependencies = [
"serde_json", "serde_json",
"swoosh", "swoosh",
"textsender_models", "textsender_models",
"time",
"tokio", "tokio",
] ]
+1
View File
@@ -10,6 +10,7 @@ futures = { version = "0.3.32" }
reqwest = { version = "0.13.4", features = ["json", "stream", "multipart"] } reqwest = { version = "0.13.4", features = ["json", "stream", "multipart"] }
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = { version = "1.0.150" } serde_json = { version = "1.0.150" }
time = { version = "0.3.49", features = ["formatting", "macros", "parsing", "serde"] }
textsender_models = { git = "ssh://git@git.kundeng.us/phoenix/textsender_models.git", tag = "v0.4.3-29-720f3d8f75-111" } textsender_models = { git = "ssh://git@git.kundeng.us/phoenix/textsender_models.git", tag = "v0.4.3-29-720f3d8f75-111" }
swoosh = { git = "ssh://git@git.kundeng.us/phoenix/swoosh.git", tag = "v0.4.1-10-6bdec12084-871" } swoosh = { git = "ssh://git@git.kundeng.us/phoenix/swoosh.git", tag = "v0.4.1-10-6bdec12084-871" }
+6 -5
View File
@@ -3,6 +3,7 @@ pub const SECONDS_TO_SLEEP: u64 = 5;
pub const APP_NAME: &str = "catapult"; pub const APP_NAME: &str = "catapult";
#[derive(Clone, Debug)]
pub struct App { pub struct App {
pub api_url: String, pub api_url: String,
pub auth_url: String, pub auth_url: String,
@@ -21,8 +22,8 @@ pub async fn load_app() -> Result<App, std::io::Error> {
let service_passphrase_env = envy::environment::get_env("SERVICE_PASSPHRASE").await; let service_passphrase_env = envy::environment::get_env("SERVICE_PASSPHRASE").await;
let envs = vec![ let envs = vec![
auth_url_env,
api_url_env, api_url_env,
auth_url_env,
service_username_env, service_username_env,
service_passphrase_env, service_passphrase_env,
]; ];
@@ -41,10 +42,10 @@ pub async fn load_app() -> Result<App, std::io::Error> {
if envs_valid { if envs_valid {
match textsender_models::config::auxiliary::load_config().await { match textsender_models::config::auxiliary::load_config().await {
Ok(twilio_config) => Ok(App { Ok(twilio_config) => Ok(App {
api_url: String::new(), api_url: envs[0].value.clone(),
auth_url: String::new(), auth_url: envs[1].value.clone(),
service_username: String::new(), service_username: envs[2].value.clone(),
service_passphrase: String::new(), service_passphrase: envs[3].value.clone(),
twilio_config: twilio_config, twilio_config: twilio_config,
}), }),
Err(err) => Err(err), Err(err) => Err(err),
+36 -1
View File
@@ -14,8 +14,43 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
}; };
println!("App: {app:?}");
let auth = catapult::service::auth::Auth { app: app.clone() };
let token = match auth.get_token().await {
Ok(token) => token,
Err(err) => {
eprintln!("Error getting token");
eprintln!("Error: {err:?}");
std::process::exit(-1);
}
};
println!("Access token: {}", token.access_token);
let mut svc = catapult::service::core::Service {
app: app.clone(),
token: token,
};
loop { loop {
catapult::service::do_the_work(&app).await; // TODO: Need to update textsender_models. The token module needs updating.
// The LoginResult needs expires_in populated
if catapult::service::auth::has_token_expired(&svc.token).await {
match auth.get_refresh_token(&svc.token).await {
Ok(refresh) => {
println!("Refresh token: {refresh:?}");
svc.token = refresh;
}
Err(err) => {
eprintln!("Error: {err:?}");
std::process::exit(-1);
}
}
}
svc.do_the_work().await;
tokio::time::sleep(tokio::time::Duration::from_secs( tokio::time::sleep(tokio::time::Duration::from_secs(
catapult::app::SECONDS_TO_SLEEP, catapult::app::SECONDS_TO_SLEEP,
)) ))
-22
View File
@@ -1,22 +0,0 @@
pub async fn do_the_work(_app: &crate::app::App) {
println!("Do some work");
todo!(
r#"
Need to finish this
This function should fetch a token, check if the token is valid, and obtain a refresh token if
it has expired.
Then do the real work.
1. Check the queue
2. If there is an item, get the data,
3. Evaluate if it is schedulable
4. Get the events and iterate each one
5. starting with the message
6. Get the contact
7. Send the message
8. Record the message event response
"#
);
}
+29
View File
@@ -0,0 +1,29 @@
pub struct Service {
pub app: crate::app::App,
pub token: textsender_models::token::Login,
}
impl Service {
pub async fn do_the_work(&mut self) {
println!("Do some work");
todo!(
r#"
Need to finish this
This function should fetch a token, check if the token is valid, and obtain a refresh token if
it has expired.
Then do the real work.
1. Check the queue
2. If there is an item, get the data,
3. Evaluate if it is schedulable
4. Get the events and iterate each one
5. starting with the message
6. Get the contact
7. Send the message
8. Record the message event response
"#
);
}
}
+120
View File
@@ -0,0 +1,120 @@
pub mod core;
pub mod auth {
pub struct Auth {
pub app: crate::app::App,
}
impl Auth {
pub async fn get_token(&self) -> Result<textsender_models::token::Login, std::io::Error> {
let client = reqwest::Client::new();
let endpoint = String::from("api/v1/service/login");
let api_url = format!("{}/{endpoint}", self.app.auth_url);
println!("Url: {api_url:?}");
let payload = serde_json::json!({
"username": &self.app.service_username,
"passphrase": &self.app.service_passphrase,
});
println!("Payload: {payload:?}");
match client.post(api_url).json(&payload).send().await {
Ok(response) => match response.status() {
reqwest::StatusCode::OK => {
println!("Response: {response:?}");
match response.text().await {
Ok(val) => match parse_token_response(&val).await {
Ok(login_result) => Ok(login_result),
Err(err) => Err(err),
},
Err(err) => Err(std::io::Error::other(err)),
}
}
_ => Err(std::io::Error::other("Error getting token")),
},
Err(err) => Err(std::io::Error::other(err.to_string())),
}
}
pub async fn get_refresh_token(
&self,
login_result: &textsender_models::token::Login,
) -> Result<textsender_models::token::Login, std::io::Error> {
let client = reqwest::Client::new();
let endpoint = String::from("api/v1/token/refresh");
let api_url = format!("{}/{endpoint}", self.app.auth_url);
println!("Url: {api_url:?}");
let payload = serde_json::json!({
"access_token": login_result.access_token
});
match client.post(api_url).json(&payload).send().await {
Ok(response) => match response.status() {
reqwest::StatusCode::OK => {
println!("Response: {response:?}");
match response.text().await {
Ok(val) => match parse_token_response(&val).await {
Ok(login_result) => Ok(login_result),
Err(err) => Err(err),
},
Err(err) => Err(std::io::Error::other(err)),
}
}
_ => Err(std::io::Error::other("Error getting token")),
},
Err(err) => Err(std::io::Error::other(err.to_string())),
}
}
}
async fn parse_token_response(
response: &String,
) -> Result<textsender_models::token::LoginResult, std::io::Error> {
match serde_json::from_str::<serde_json::Value>(response) {
Ok(j) => {
println!("Good");
let message = &j["message"];
let data = &j["data"];
println!("Message: {message:?}");
println!("Data: {data:?}");
match j.get("data") {
Some(serde_json::Value::Array(lrs)) => {
if lrs.is_empty() {
Err(std::io::Error::other("Error response is empty"))
} else {
let lr = lrs[0].clone();
let ll: textsender_models::token::LoginResult =
match serde_json::from_value(lr) {
Ok(lr) => lr,
Err(err) => {
return Err(std::io::Error::other(err.to_string()));
}
};
Ok(ll)
}
}
_ => Err(std::io::Error::other("Error parsing response")),
}
}
Err(err) => Err(std::io::Error::other(err.to_string())),
}
}
pub async fn has_token_expired(token: &textsender_models::token::LoginResult) -> bool {
let now = time::OffsetDateTime::now_utc();
let expire = match time::OffsetDateTime::from_unix_timestamp(token.expires_in) {
Ok(res) => res,
Err(err) => {
eprintln!("Error: {err:?}");
return false;
}
};
now > expire
}
}